thread hilos concurrent java concurrency multithreading project-planning

hilos - thread executor java



Cómo lidiar con la concurrencia antes de comenzar a codificar (9)

Estoy a medio camino de la programación de un programa Java, y estoy en la etapa en la que estoy depurando muchos más problemas de concurrencia de los que me gustaría tratar.

Tengo que preguntar: ¿cómo lidias con los problemas de concurrencia al configurar tu programa mentalmente? En mi caso, es para un juego relativamente simple, sin embargo, los problemas con los hilos siguen apareciendo: cualquier solución rápida casi seguramente conduce a un nuevo problema.

Hablando en términos muy generales, ¿qué técnicas debo usar para decidir cómo debería "fluir" mi aplicación sin que todos mis hilos se enreden?


  1. Intente usar colecciones del paquete java.util.concurrent o, incluso, colecciones inmutables de Google Collections.
  2. Lee sobre el uso de bloques sincronizados


Cuantos menos subprocesos tengas, menor será el estado que compartan y cuanto más simple sea su patrón de interacción en este estado compartido, más sencilla será tu vida.

Usted dice que las listas están lanzando ConcurrentModificationException. Supongo que sus listas son accedidas por hilos separados. Entonces, lo primero que debes preguntarte es si esto es necesario. ¿No es posible que el segundo hilo funcione en una copia de la lista?

Si de hecho es necesario que los subprocesos accedan a la lista simultáneamente, el bloqueo de la lista durante todo el recorrido podría ser una opción (los iteradores se invalidan si la lista se modifica por cualquier otro medio que no sea ese iterador). Por supuesto, si hace otras cosas mientras recorre la lista, este recorrido puede llevar mucho tiempo, y el bloqueo de otros subprocesos puede amenazar la vida del sistema.

También tenga en cuenta que si la lista es un estado compartido, también lo es su contenido, por lo tanto, si tiene la intención de evitar el bloqueo copiando la lista, asegúrese de realizar una copia en profundidad, o demuestre que los objetos contenidos en la lista son seguros para subprocesos. .


Depende de lo que hagan tus hilos. Normalmente, los programas tienen un hilo principal que hace que los hilos de pensamiento y de trabajo realicen tareas paralelas (temporizadores, manejo de cómputos largos en una GUI, etc.) Pero su aplicación puede ser diferente, depende de su diseño. ¿Para qué usas hilos? ¿Qué bloqueos tienes para proteger las estructuras de datos compartidas? Si utiliza varios bloqueos, ¿tiene un único orden en el que se bloquea para evitar puntos muertos?


Desde la perspectiva del diseño, me ha resultado útil dibujar diagramas de secuencia en los que las acciones de cada hilo están codificadas por colores (es decir, cada hilo tiene su propio color). El uso del color de esta manera puede ser un uso no estándar de un diagrama de secuencia, pero es bueno para dar una visión general de cómo y dónde interactúan los hilos.

Sin embargo, como otros han mencionado, reducir la cantidad de hilos en su diseño al mínimo absoluto que necesita para funcionar correctamente también ayudará mucho.


Es posible que la naturaleza de múltiples subprocesos de su aplicación sea una pista falsa, con respecto a las Excepciones de Modificaciones Concurrentes que mencionó: hay otras maneras en que puede obtener una Excepción de Modificaciones Concurrentes que no necesariamente involucre múltiples hebras. Considera lo siguiente:

List<Item> items = new ArrayList<Item>(); //... some code adding items to the list for (Item item : items) { if(item.isTheOneIWantToRemove()) { items.remove(item); //This will result in a ConcurrentModificationException } }

Cambiar su bucle for a un bucle con un iterador, o un valor de índice creciente resuelve el problema:

for (Iterator<String> it = items.iterator(); it.hasNext();) { if(item.isTheOneIWantToRemove()) { it.remove(); //No exception thrown } }

o

for (int i = 0; i < items.size(); i++) { if(item.isTheOneIWantToRemove()) { items.remove(items.get(i)); //No exception thrown } }


La concurrencia se reduce a la gestión del estado compartido.

"Todos los problemas de concurrencia se reducen a coordinar el acceso al estado mutable. Cuanto menor sea el estado mutable, más fácil será garantizar la seguridad del subproceso". - Java concurrencia en la práctica

Así que la pregunta que debes hacerte es:

  • ¿Cuáles son los datos compartidos inherentes que necesitará mi aplicación?
  • ¿Cuándo puede funcionar un hilo en una instantánea de los datos, es decir, funciona momentáneamente en un clon de los datos compartidos?
  • ¿Puedo identificar patrones conocidos y usar abstracción de nivel superior en lugar de bloqueos de bajo nivel y coordinación de hilos, por ejemplo, colas, ejecutor, etc.?
  • Piense en un esquema de bloqueo global para evitar el interbloqueo y tener una adquisición consistente de bloqueos

El enfoque más simple para administrar el estado compartido es serializar cada acción. Este enfoque general resulta, sin embargo, en una contención de alto bloqueo y un rendimiento deficiente. La gestión de la concurrencia puede verse como un ejercicio de optimización en el que intenta reducir la contención. Así que las siguientes preguntas son:

  • ¿Cómo sería el enfoque más simple?
  • ¿Cuál es la elección simple que puedo hacer para reducir la contención (posiblemente con un bloqueo de grano fino) y mejorar el rendimiento sin hacer que la solución sea demasiado complicada?
  • ¿Cuándo me estoy yendo demasiado fino, es decir, la complejidad introducida no vale la pena el aumento de rendimiento?

Una gran cantidad de enfoques para reducir la contención se basan en algún tipo de intercambio entre lo que sería necesario para imponer el comportamiento correcto y lo que es posible para reducir la contención.

  • ¿Dónde puedo relajar algunas restricciones y aceptar que a veces las cosas no serán 100% correctas (por ejemplo, un contador)?
  • ¿Puedo ser optimista y lidiar con conflictos solo cuando ocurren modificaciones concurrentes (p. Ej., Usando la marca de tiempo y la lógica de reintento, eso es lo que hace TM)?

Tenga en cuenta que nunca trabajé en un juego, solo en la parte del servidor de las aplicaciones empresariales. Puedo imaginar que puede ser muy diferente.


Utilizo estructuras de datos inmutables tanto como sea posible. Casi la única vez que utilizo estructuras mutables es cuando tengo que hacerlo, por ejemplo, con una biblioteca que salvará un montón de trabajo. Incluso entonces trato de encapsular esa biblioteca en una estructura inmutable. Si las cosas no pueden cambiar, entonces hay menos de qué preocuparse.

Debo agregar que algunas cosas a tener en cuenta en sus esfuerzos futuros son los modelos STM y Actor. Ambos enfoques de concurrencia muestran un progreso muy bueno. Si bien hay algunos gastos generales para cada uno, dependiendo de la naturaleza de su programa, puede que no sea un problema.

Editar:

Aquí hay algunos enlaces a algunas bibliotecas que podría usar en su próximo proyecto. Hay Deuce STM que, como su nombre lo indica, es una implementación de STM para Java. Luego está el ActorFoundry que, como su nombre lo indica, es un modelo Actor para Java. Sin embargo, no puedo dejar de hacer el complemento para Scala con su modelo Actor incorporado.


Lea acerca de la concurrencia, o mejor aún, tome un curso de posgrado en programación concurrente si todavía está en la universidad. Ver Los Tutoriales de Java: Lección: Concurrencia . Un libro famoso para la concurrencia de Java es Java concurrencia en la práctica . Java tiene mucho incorporado en el marco para tratar los problemas de concurrencia, incluidas las colecciones concurrentes y synchronized métodos synchronized .

Concurrencia Java en la práctica http://ecx.images-amazon.com/images/I/51Hx%2Bg4Q6QL._BO2,204,203,200-76_AA240_SH20_OU01_.jpg