JUC practical explanation-teacher Li Hefei

[From the JUC video of station B: https://www.bilibili.com/video/BV14W411u7gB ] The full version of the actual combat tutorial on the source of the JUC in Silicon Valley (the java juc thread)

1. Introduction to Java JUC

JDK5 provided in  the java.util.concurrent  (referred to as the JUC) package, increased commonly useful in concurrent programming tools in this package, similar to define custom thread subsystems, including thread pools, asynchronous IO And lightweight task framework. Provide an adjustable and flexible thread pool. It also provides a Collection implementation designed for use in a multithreaded context, etc.

2. Volatile keyword-memory visibility

JVM provides an independent cache for each thread to improve efficiency. This cache, which is private to each thread, is called "local memory". Memory Visibility means that after a thread modifies the value of a shared variable, other threads must be able to immediately know the modification. Visibility error means that when the read operation and the write operation are executed in different threads, we cannot ensure that the thread performing the read operation can see the value newly written by other writing threads in real time. We can ensure that objects are released safely through synchronization. In addition, we can also use a more lightweight volatile variable. Java provides a weaker synchronization mechanism, namely volatile variables, to ensure that the update operation of variables can be immediately notified to other threads.

Volatile can be regarded as a lightweight lock, but it is somewhat different from locks:

(1) For multithreading, it is not a mutually exclusive relationship. (2) It only guarantees "memory visibility" and cannot guarantee "atomic operation" of variable states.

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;
    }
}

        

The flag read by the mian thread is always false, so the printed result is flag = true, and the program does not end.

Solution: volatile, when multiple threads perform operations to share data, the visibility of data in memory can be guaranteed.

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. Atomic variable-CAS algorithm

Instances of the classes AtomicBoolean, AtomicInteger, AtomicLong and AtomicReference each provide access and update to individual variables of the corresponding type. Each class also provides appropriate utility methods for that type.

The AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray classes further extend atomic operations and provide support for these types of arrays. These classes are also notable for providing volatile access semantics for their array elements, which is not supported for ordinary arrays.

Core method: boolean compareAndSet(expectedValue, updateValue)

It provides some common type atomic operation at java.util.concurrent.atomic package: AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference AtomicIntegerArray, AtomicLongArray, AtomicMarkableReference, AtomicReferenceArray, AtomicStampedReference
specific methods, API documentation can [ https: // www. matools.com/api/java8 ].

i++ atomicity problem, first read i and then ++, the operation is separated, there is a synchronization security problem

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++;
    }
}

Atomic variables commonly used atomic variables are provided under the java.util.concurrent.atomic package after JDK5.

(1) Volatile guarantees memory visibility

(2) CAS algorithm guarantees the atomicity of data

The CAS algorithm is hardware support for concurrent operation of shared data: it contains three operands: memory value V, estimated value A, updated value B: if and only when V == A, V = B, otherwise, it will not Do any operation.

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();
            }
        }
    }
}

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/cmm0401/article/details/108926071