JUC explicación práctica-maestro Li Hefei

[Del video de JUC de la estación B: https://www.bilibili.com/video/BV14W411u7gB ] La versión completa del tutorial de combate real sobre la fuente de JUC en Silicon Valley (el hilo de java juc)

1. Introducción a Java JUC

JDK5 proporcionado en  el  paquete java.util.concurrent (referido como el JUC), comúnmente más útil en herramientas de programación concurrente en este paquete, similar a definir subsistemas de subprocesos personalizados, incluyendo grupos de subprocesos, E / S asíncrona Y marco de tareas ligero. Proporcione un grupo de hilos ajustable y flexible. También proporciona una implementación de colección diseñada para su uso en un contexto multiproceso, etc.

2. Visibilidad de memoria de palabras clave volátil

JVM proporciona una caché independiente para cada hilo para mejorar la eficiencia. Esta caché, que es privada para cada hilo, se llama "memoria local". Visibilidad de memoria significa que cuando un hilo modifica el valor de una variable compartida, otros hilos deben poder conocer inmediatamente la modificación. El error de visibilidad significa que cuando la operación de lectura y la operación de escritura se ejecutan en diferentes subprocesos, no podemos asegurar que el subproceso que realiza la operación de lectura pueda ver el valor recién escrito por otros subprocesos de escritura en tiempo real. Podemos asegurarnos de que los objetos se liberen de forma segura mediante la sincronización. Además, también podemos utilizar una variable volátil más ligera. Java proporciona un mecanismo de sincronización más débil, a saber, las variables volátiles, para garantizar que la operación de actualización de las variables pueda notificarse inmediatamente a otros subprocesos.

Volátil puede considerarse un candado ligero, pero es algo diferente de los candados:

(1) Para multiproceso, no es una relación mutuamente excluyente. (2) Solo garantiza la "visibilidad de la memoria" y no puede garantizar el "funcionamiento atómico" de los estados variables.

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo t = new ThreadDemo();
        new Thread(t).start();
        while (true) {
            if(t.isFlag()) {
                System.out.println("------");
                break;
            }
        }
    }
}
class ThreadDemo implements Runnable {
    private boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag = " + isFlag());
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

        

La bandera que lee el hilo mian siempre es falsa, por lo que el resultado impreso es flag = true y el programa no termina.

Solución: volátil, cuando varios subprocesos realizan operaciones para compartir datos, se puede garantizar la visibilidad de los datos en la memoria.

public class VolatileTest {
    public static void main(String[] args) {
        ThreadDemo t = new ThreadDemo();
        new Thread(t).start();
        while (true) {
            if(t.isFlag()) {
                System.out.println("------");
                break;
            }
        }
    }
}
class ThreadDemo implements Runnable {
    private volatile boolean flag = false;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        flag = true;
        System.out.println("flag = " + isFlag());
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}

3. Algoritmo CAS variable atómica

Las instancias de las clases AtomicBoolean, AtomicInteger, AtomicLong y AtomicReference proporcionan acceso y actualización a variables individuales del tipo correspondiente. Cada clase también proporciona métodos de utilidad apropiados para ese tipo.

Las clases AtomicIntegerArray, AtomicLongArray y AtomicReferenceArray amplían aún más las operaciones atómicas para proporcionar compatibilidad con estos tipos de matrices. Estas clases también se destacan por proporcionar semántica de acceso volátil para sus elementos de matriz, que no es compatible con matrices normales.

Método principal: compareAndSet booleano (valor esperado, valor de actualización)

Proporciona algún tipo común de operación atómica en el paquete de java.util.concurrent.atomic: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference AtomicIntegerArray, AtomicLongArray, AtomicMarkableReference, AtomicReferenceArray, AtomicStampedReference
métodos específicos, documentación de la API puede [ https: // www. matools.com/api/java8 ].

i ++ problema de atomicidad, primero lea iy luego ++, la operación está separada, hay un problema de seguridad de sincronización

public class AtomicTest {
    public static void main(String[] args) {
        Atomic a = new Atomic();
        for (int i = 0; i < 10; i++) {
            new Thread(a).start();
        }
    }
}
class Atomic implements Runnable {
    private volatile int serialNumber = 0;
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
    }
    public int getSerialNumber() {
        return serialNumber++;
    }
}

Las variables atómicas Las variables atómicas de uso común se proporcionan en el paquete java.util.concurrent.atomic después de JDK5.

(1) Volátil garantiza la visibilidad de la memoria

(2) El algoritmo CAS garantiza la atomicidad de los datos.

El algoritmo CAS es soporte de hardware para la operación concurrente de datos compartidos: contiene tres operandos: valor de memoria V, valor estimado A, valor actualizado B: si y solo si V == A, V = B; de lo contrario, no Hacer nada.

public class CompareAndSwapTest {
    public static void main(String[] args) {
        CompareAndSwap cas = new CompareAndSwap();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    int expectedValue = cas.get();
                    boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
                    System.out.println(b);
                }
            }).start();
        }
    }
}
class CompareAndSwap {
    private int value;
    // 获取内存值
    public synchronized int get(){
        return value;
    }
    // 比较
    public synchronized int compareAndSwap(int expectedValue, int newValue) {
        int oldValue = value;
        if(oldValue == expectedValue) {
            this.value = newValue;
        }
        return oldValue;
    }
    // 设置
    public synchronized boolean compareAndSet(int expectedValue, int newValue) {
        return expectedValue == compareAndSwap(expectedValue, newValue);
    }
}


public class CopyOnWriteArrayListTest {
    public static void main(String[] args) {
        HelloThread ht = new HelloThread();
        for (int i = 0; i < 2; i++) {
            new Thread(ht).start();
        }
    }
}
/**
 * CopyOnWriteArrayList写入并复制,添加操作多时,效率低,因为每次添加时都会进行复制,开销很大
 * 并发迭代操作多时可以选择
 */
class HelloThread implements Runnable {
    // 这种会出现并发修改异常
//    private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
    private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
    static {
        list.add("AA");
        list.add("BB");
        list.add("CC");
    }
    @Override
    public void run() {
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
            list.add("AA");
        }
    }
}

public class CountDownLatchTest {
    public static void main(String[] args) {
        // 5 表示其他线程的数量
        CountDownLatch latch = new CountDownLatch(5);
        LatchDemo ld = new LatchDemo(latch);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 5; i++) {
            new Thread(ld).start();
        }
        try {
            // 此处要一直等到 latch的值为0 ,就能往下执行了
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("消耗时间为:" + (end - start));
    }
}
class LatchDemo implements Runnable {
    private CountDownLatch latch;
    public LatchDemo(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override
    public void run() {
        synchronized(this) {
            try {
                for (int i = 0; i < 100; i++) {
                    if (i % 2 == 0) {
                        System.out.println(i);
                    }
                }
            } finally {
                latch.countDown();
            }
        }
    }
}

 

 

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/cmm0401/article/details/108926071
Recomendado
Clasificación