Antes de leer este artículo, primero puede leer " Introducción y uso del paquete atómico de Java Multhrithreaded " para aprender sobre el contenido relevante del paquete atómico.
1. ¿Qué es atómico?
La palabra atómica tiene algo que ver con los átomos, que alguna vez se consideró la unidad de la materia más pequeña. Atomic en una computadora significa que no se puede dividir en varias partes. Si un código se considera atómico, significa que el código no se puede interrumpir durante la ejecución. En términos generales, el hardware proporciona instrucciones atómicas y el software proporciona para implementar métodos atómicos (un hilo no se interrumpirá después de ingresar el método hasta que se complete su ejecución)
En la plataforma X86, la CPU proporciona los medios para bloquear el bus durante la ejecución de instrucciones. Hay un #hlockpin principal en el chip CPU. Si el "bloqueo" del prefijo se agrega a una instrucción en el programa de lenguaje de ensamblaje, el código de la máquina de ensamblaje hará que la CPU disminuya el potencial de #HLockPin al ejecutar esta instrucción, y la liberará hasta el final de esta instrucción, bloqueando así el bus. De esta manera, otras CPU en el mismo bus no pueden acceder a la memoria a través del bus por el momento, asegurando la atomicidad de esta instrucción en un entorno multiprocesador.
2. Variables atómicas en java.util.concurrent
Ya sea directo o indirecto, casi todas las clases en el paquete java.util.concurrent usan variables atómicas, no sincronización. Las clases como concurrentLinkedQueue también usan variables atómicas para implementar directamente el algoritmo sin espera, mientras que las clases como ConcurrentHashmap usan ReentrantLock para bloquear cuando sea necesario. Reentrantlock luego usa variables atómicas para mantener la cola de hilo esperando el bloqueo.
Sin mejoras JVM en JDK5.0, estas clases no se construirán, lo que expone (a la biblioteca de clases, no a la clase de usuario) interfaces para acceder a primitivas de sincronización a nivel de hardware. Las clases de variables atómicas y otras clases en java.util.concurrent luego exponen estas funciones a la clase de usuario
clase atómica de java.util.concurrent.atomic
Este paquete proporciona un conjunto de clases atómicas. Su característica básica es que en un entorno múltiple, cuando múltiples hilos ejecutan métodos contenidos en instancias de estas clases al mismo tiempo, es exclusivo, es decir, cuando un hilo ingresa el método y ejecuta las instrucciones en él, no se interrumpirá por otros hilos, y otros hilos son como bloqueos de espín. Hasta que se ejecute el método, el JVM seleccionará otro hilo de la cola de espera para ingresar. Esto es solo una comprensión lógica. De hecho, se implementa con la ayuda de instrucciones de hardware relevantes y no bloqueará los subprocesos (o solo se bloquea a nivel de hardware). Las clases se pueden dividir en 4 grupos
Atomicboolean, AtomicInteger, Atomiclong, Atomicreference
AtomicIntegerArray, AtomicLongarrray
AtomicLongfieldupdater, AtomicIntegerfieldupdater, AtomicreferenceFieldupdater
AtomicMarkableReference, AtomicSpampedReference, AtomicreferencearRay
Entre ellos, Atomicboolean, AtomicInteger, Atomiclong, Atomicreference son similares.
En primer lugar, los Atomicboolean, AtomicInteger, Atomiclong, Atomicreference, las API internos son similares: tome un ejemplo de atomicreferencia
Crear una pila segura de hilo con atomicreference
clase pública LinkedStack <T> {private atomicreference <node <t>> stacks = new Atomicreference <node <t> (); public t Push (t e) {node <t> oldNode, newnode; mientras que (verdadero) {// El procesamiento aquí es muy especial, y debe ser el caso. OldNode = stacks.get (); newnode = new node <t> (e, oldNode); if (stacks.compeEset (oldNode, newnode)) {return e;}}} public t pop () {node <t> oldNode, newNode; while (true) {oldNode = stacks.get (); newnode = OldNode.Ext; (stacks.compeeandSet (OldNode, NewNode)) {return oldNode.Object;}}} privado nodo de clase final estática <t> {objeto privado t; nodo privado <t> next; private node (t objeto, nodo <t> next) {this.object = object; this.next = next;}}}Luego concéntrese en la actualización atómica del campo.
AtomicIntegerFieldUpdater <t>/AtomicLongfieldupdater <t>/AtomicreferenceFieldUpdater <t, V> es el valor de un campo basado en la reflexión.
La API correspondiente también es muy simple, pero también tiene algunas limitaciones.
(1) ¡El campo debe ser de tipo volátil! ¿Qué es volátil? Verifique " Explicación detallada de las palabras clave volátiles en Java "
(2) El tipo de descripción del campo (modificador público/protegido/predeterminado/privado) es consistente con la relación entre la persona que llama y el campo de objeto de operación. Es decir, la persona que llama puede operar directamente el campo de objeto y luego reflejar y realizar operaciones atómicas. Sin embargo, para los campos de la clase principal, la subclase no se puede operar directamente, aunque la subclase puede acceder a los campos de la clase principal.
(3) Solo puede ser una variable de instancia, no una variable de clase, es decir, no puede agregar palabras clave estáticas.
(4) Solo se puede modificar variables, y no se puede convertir en variables finales, porque la semántica de final no está modificada. De hecho, la semántica del conflicto final y volátil, y estas dos palabras clave no pueden existir al mismo tiempo.
(5) Para AtomicIntegerFieldupdater y AtomicLongFieldupdater, solo pueden modificar campos de tipo int/long, y no pueden modificar su tipo de envoltorio (entero/largo). Si desea modificar el tipo de envoltura, debe usar AtomicreferenceFieldUpdater.
El método de operación se describe en el siguiente ejemplo.
import java.util.concurrent.atomic.atomicIntegerFieldUpdater; public class AtomicInTegerFieldUpdaterDemo {Class Demodata {public Volatile int value1 = 1; volatile int value2 = 2; protegido Volatile int value3 = 3; privado Volátil int value4 = 4;} atomicIntepieldUpdater <demodata> string flound (string FieldAntam) {return atomiCintegerFieldupdater.newupdater (demodata.class, fieldName);} void doit () {Demodata data = new Demodata (); System.out.println ("1 ==>"+getupdater ("value1"). getandset (datos, 10)); system.println ("3 ==> "+getupdater (" value2 "). incrementandget (data)); system.out.println (" 2 ==> "+getupdater (" value3 "). decrementAndget (data)); system.out.println (" true ==> "+getupdater (" value4 "). compareanSet (data, 4, 5));} Public static void main (stats (String [] args (] args (] args) {AtomicInTEGERFIELDUPDATERDEMO DEMO = new AtomicInTegerFieldUpDaterDemo (); Demo.doit ();}}En el ejemplo anterior, el valor de campo 3/valor4 de demodata no es visible para la clase AtomicInTegerFieldupdaterDemo, por lo que su valor no puede modificarse directamente a través de la reflexión.
Un par de <objeto, booleano> descrito por la clase AtomicMarkableSerference puede modificar atómicamente el valor de objeto o booleano. Esta estructura de datos es más útil en algunas descripciones de caché o estado. Esta estructura puede mejorar efectivamente el rendimiento al modificar el objeto/booleano individual o simultáneamente.
La clase AtomicStampedReference mantiene las referencias de objetos con "indicadores" enteros y se puede actualizar atómicamente. En comparación con el <objeto, booleano> de la clase AtomicMarkableSerference, AtomicStampedReference mantiene una estructura de datos similar a <Object, int>, que en realidad es un recuento concurrente de objetos (referencias). Pero a diferencia de AtomicInteger, esta estructura de datos puede llevar una referencia de objeto y puede realizar operaciones atómicas en este objeto y contar al mismo tiempo.
El "problema de ABA" se mencionará al final de este artículo, y AtomicMarkableReference/AtomicStampedReference es útil para resolver el "problema ABA".
Iii. El papel de atómico
Esto permite que la operación de datos únicos se atomice
Cree un código complejo y sin bloqueo utilizando clases atómicas
Generalmente se considera que acceder a 2 o más variables atómicas (o realizar 2 o más operaciones en una sola variable atómica) requiere sincronización para que estas operaciones puedan usarse como una unidad atómica.
Sin algoritmo de bloqueo y sin espera
Los algoritmos de concurrencia basados en CAS (comparación con comparación) se llaman algoritmos sin bloqueo porque los hilos ya no tienen que esperar el bloqueo (a veces llamado mutexes o piezas críticas, dependiendo de la terminología de la plataforma de subprocesos). Si la operación de CAS tiene éxito o falla, en cualquier caso, se completa dentro de un tiempo predecible. Si CAS falla, la persona que llama puede volver a intentar la operación CAS o tomar otras operaciones adecuadas.
Si cada hilo continúa funcionando cuando otros hilos se retrasan (o incluso fallan), se puede decir que el algoritmo está libre de espera. En contraste, el algoritmo sin bloqueo requiere que solo un cierto hilo siempre realice la operación. (Otra definición de no espera es asegurarse de que cada hilo calcule correctamente sus propias operaciones en sus pasos limitados, independientemente de las operaciones, el tiempo, el crossover o la velocidad de otros subprocesos. Este límite puede ser una función del número de subprocesos en el sistema; por ejemplo, si hay 10 subprocesos, cada subproceso realiza un Cascounter.
En los últimos 15 años, las personas han realizado una extensa investigación sobre algoritmos sin candado y sin bloqueo (también conocidos como algoritmos sin bloqueo), y muchas personas han descubierto algoritmos sin bloqueo en las estructuras de datos generales. Los algoritmos que no son de bloqueo se usan ampliamente en el sistema operativo y el nivel JVM, realizando tareas como el roscado y la programación de procesos. Aunque su implementación es más compleja, tienen muchas ventajas sobre los algoritmos alternativos basados en el bloqueo: pueden evitar peligros como la inversión prioritaria y el punto muerto, la competencia es más barata, la coordinación ocurre en un nivel de granularidad más fino, lo que permite un mayor grado de paralelismo, etc.
Común:
Mostrador sin bloqueo
Pila sin bloqueo concurrentstack
Lista vinculada sin bloqueo concurrentLinkedQueue
Preguntas de ABA:
Porque antes de cambiar V, el CAS pregunta principalmente "si el valor de V sigue siendo un", antes de leer V por primera vez y realizar operaciones de CAS en V, cambiar el valor de A a B y luego volver a confundir el algoritmo basado en CAS. En este caso, la operación CAS tendrá éxito, pero en algunos casos el resultado puede no ser lo que esperaba. Este tipo de problema se llama problema ABA, que generalmente se maneja asociando una etiqueta o número de versión con cada valor que se realizará en la operación CAS y actualiza atómicamente los valores y etiquetas. La clase AtomicStampedReference admite este método.
Resumir
Lo anterior es toda la explicación detallada de este artículo sobre las variables atómicas operativas y las clases atómicas en el paquete atómico de múltiples subprocesos Java. Espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a este sitio:
Programación de Java: el punto muerto de múltiples subprocesos y la comunicación entre los hilos son un código simple
Programación de múltiples subconjuntos de Java Pequeño ejemplo simula Sistema de estacionamiento
Una breve discusión sobre las ventajas y los ejemplos de código de Java multithreading
Si hay alguna deficiencia, deje un mensaje para señalarlo.