java multithreading concurrency thread-safety thread-confinement

java - Encadenamiento de subprocesos



multithreading concurrency (8)

Estoy leyendo Java Concurrency in Practice y estoy algo confundido con el concepto de confinamiento de hilos. El libro dice que

Cuando un objeto está confinado a un subproceso, dicho uso es automáticamente seguro para subprocesos incluso si el objeto confinado en sí no es

Entonces, cuando un objeto está confinado a un hilo, ¿ningún otro hilo puede tener acceso a él? ¿Es eso lo que significa estar confinado a un hilo? ¿Cómo se puede mantener un objeto confinado a un hilo?

Editar: ¿Pero qué sucede si aún quiero compartir el objeto con otro hilo? Digamos que después de que el hilo A termina con el objeto O, el hilo B quiere acceder a O. En este caso, ¿O puede seguir limitado a B después de que A termine con él?

Usar una variable local es un ejemplo seguro pero eso solo significa que no compartes tu objeto con otro hilo (AT ALL). En el caso del grupo de conexiones JDBC, ¿no pasa una conexión de un hilo a otro una vez que se realiza un hilo con esa conexión (totalmente desorientado porque nunca usé JDBC)?


Entonces, cuando un objeto está confinado a un hilo, ¿ningún otro hilo puede tener acceso a él?

Eso es lo que significa el confinamiento de subprocesos: el objeto solo puede tener acceso a un solo subproceso.

¿Es eso lo que significa estar confinado a un hilo?

Véase más arriba.

¿Cómo se puede mantener un objeto confinado a un hilo?

El principio general es no poner la referencia en algún lugar que permita que otro hilo lo vea. Es un poco complicado enumerar un conjunto de reglas que asegurarán esto, pero (por ejemplo) si

  • creas un nuevo objeto, y
  • nunca se asigna la referencia del objeto a una instancia o variable de clase, y
  • nunca llamas a un método que hace esto para la referencia,
  • entonces el objeto será confinado en hilo.

Entonces, cuando un objeto está confinado a un hilo, ¿ningún otro hilo puede tener acceso a él?

No, es al revés: si se asegura de que ningún otro subproceso tenga acceso a un objeto, se dice que ese objeto está confinado a un único subproceso.

No hay un mecanismo de nivel de lenguaje o JVM que confine un objeto a un solo hilo. Simplemente debe asegurarse de que ninguna referencia al objeto escapa a un lugar al que pueda accederse otro hilo. Hay herramientas que ayudan a evitar el filtrado de referencias, como la clase ThreadLocal , pero nada que garantice que no se filtre ninguna referencia en ningún lugar.

Por ejemplo: si la única referencia a un objeto proviene de una variable local, entonces el objeto está definitivamente confinado a un único hilo, ya que otros hilos nunca pueden acceder a las variables locales.

De forma similar, si la única referencia a un objeto proviene de otro objeto que ya se ha comprobado que está confinado a un solo hilo, entonces ese primer objeto se limita al mismo hilo.

Edición de anuncios: en la práctica, puede tener un objeto al que solo se accede por un único hilo durante su vida útil, pero para el cual ese único hilo cambia (un objeto de conexión JDBC de un grupo de conexiones es un buen ejemplo).

Sin embargo, probar que un objeto de este tipo solo se accede por un único hilo es mucho más difícil que probarlo para un objeto que está confinado a un solo hilo durante toda su vida.

Y, en mi opinión, esos objetos nunca están realmente "confinados en un solo hilo" (lo que implicaría una gran garantía), sino que podría decirse que "deben ser utilizados por un solo hilo a la vez".


El ejemplo más obvio es el uso de almacenamiento local de subprocesos. Vea el ejemplo a continuación:

class SomeClass { // This map needs to be thread-safe private static final Map<Thread,UnsafeStuff> map = new ConcurrentHashMap<>(); void calledByMultipleThreads(){ UnsafeStuff mystuff = map.get(Thread.currentThread()); if (mystuff == null){ map.put(Thread.currentThread(),new UnsafeStuff()); return; }else{ mystuff.modifySomeStuff(); } } }

El UnsafeStuff objeta a sí mismo "podría ser compartido" con otros hilos en el sentido de que si Thread.currentThread() algún otro hilo en lugar de Thread.currentThread() en tiempo de ejecución al método get del mapa, obtendrías objetos que pertenecen a otros hilos. Pero estás eligiendo no hacerlo . Este es "uso que está limitado a un hilo". En otras palabras, las condiciones de tiempo de ejecución son tales que los objetos nunca se comparten entre diferentes subprocesos.

Por otro lado, en el ejemplo siguiente, el objeto se limita automáticamente a un hilo, por lo que el "objeto mismo" se limita al hilo. Esto es en el sentido de que es imposible obtener referencia de otros hilos sin importar la condición de tiempo de ejecución:

class SomeClass { void calledByMultipleThreads(){ UnsafeStuff mystuff = new UnsafeStuff(); mystuff.modifySomeStuff(); System.out.println(mystuff.toString()); } }

Aquí, el UnsafeStuff se asigna dentro del método y sale del alcance cuando el método retorna. En otras palabras, la especificación de Java garantiza de forma estática que el objeto siempre se limita a un hilo. Por lo tanto, no es la condición de tiempo de ejecución o la forma en que la usa lo que garantiza el confinamiento, sino más las especificaciones de Java.

De hecho, la JVM moderna a veces asigna tales objetos en la pila, a diferencia del primer ejemplo (no lo he comprobado personalmente, pero no creo que al menos las JVM actuales lo hagan).

Sin embargo, en otras palabras, en el primer ejemplo, la JVM no puede estar segura si el objeto está confinado dentro de un hilo simplemente mirando dentro de calledByMultipleThreads() (quién sabe qué otros métodos están jugando con SomeClass.map ). En el último ejemplo, puede.

Editar: ¿Pero qué sucede si aún quiero compartir el objeto con otro hilo? Digamos que después de que el hilo A termina con el objeto O, el hilo B quiere acceder a O. En este caso, ¿O puede seguir limitado a B después de que A termine con él?

No creo que se llame "confinado" en este caso. Cuando hace esto, solo está asegurando que no se acceda a un objeto al mismo tiempo. Así es como funciona la concurrencia EJB. Aún debe "publicar de forma segura" el objeto compartido en cuestión en los hilos.


Eso es exactamente lo que significa. Al objeto en sí se accede solo por un hilo y, por lo tanto, es seguro para subprocesos. ThreadLocal objetos ThreadLocal son un tipo de objetos que están vinculados a un único hilo


Quiero decir que solo el código que se ejecuta en un subproceso tiene acceso al objeto.

Cuando este es el caso, el objeto no necesita ser "seguro para subprocesos"


Supongo que eso es lo que quiero decir. Como crear un objeto dentro del método de run y no pasar la referencia a ninguna otra instancia.

Ejemplo simple:

public String s; public void run() { StringBuilder sb = new StringBuilder(); sb.append("Hello ").append("world"); s = sb.toString(); }

La instancia de StringBuilder es segura para subprocesos porque está confinada al subproceso (que ejecuta este método de ejecución)


Una forma es el "confinamiento de la pila" en el cual el objeto es una variable local confinada a la pila del hilo, por lo que ningún otro hilo puede acceder a ella. En el método a continuación, la list es una variable local y no escapa del método. La lista no tiene que ser enhebrable porque está confinada a la pila del hilo de ejecución. Ningún otro hilo puede modificarlo.

public String foo(Item i, Item j){ List<Item> list = new ArrayList<Item>(); list.add(i); list.add(j); return list.toString(); }

Otra forma de limitar un objeto a un hilo es mediante el uso de una variable ThreadLocal que permite que cada hilo tenga su propia copia. En el siguiente ejemplo, cada subproceso tendrá su propio objeto DateFormat y, por lo tanto, no tendrá que preocuparse por el hecho de que DateFormat no es seguro para subprocesos, ya que no será accedido por varios subprocesos.

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){ @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyyMMdd"); } };

Otras lecturas


Ver: http://codeidol.com/java/java-concurrency/Sharing-Objects/Thread-Confinement/

Un medio más formal de mantener el confinamiento del hilo es ThreadLocal, que le permite asociar un valor por hilo con un objeto que retiene valor. Thread-Local proporciona get y set accessormethods que mantienen una copia separada del valor para cada hilo que lo usa, por lo que un get devuelve el valor pasado más reciente para establecer desde el hilo actualmente en ejecución.

Tiene una copia del objeto por un hilo, el hilo A no puede acceder a la copia del hilo B y rompe sus invariantes si lo haces especialmente (por ejemplo, asigna el valor de ThreadLocal a la variable estática o lo expone usando otros métodos)