Q:

Implement a container that provides two methods add, size, write two threads:

Thread 1, add 10 elements to the container

Thread 2 monitors the number of elements in real time. When the number reaches 5, thread 2 gives a hint and ends

Preliminary thoughts:

Procedure 1

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the 2020-07-19 * parts@Description: * /
public class T01_withoutVolatile {
    List list = new ArrayList();
    
    public void add(Object o){
        list.add(0);
    }
    
    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T01_withoutVolatile c = new T01_withoutVolatile();
        new Thread(()->{
            for (int i=0; i<10; i++){ c.add(new Object());
                System.out.println("add "+i);
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch(InterruptedException e) { e.printStackTrace(); }}},"t1").start();
        new Thread(()->{
            while (true) {if(c.size()==5) {break;
                }
            }
            System.out.println("T2 end");
        },"t2").start(); }}Copy the code

Running results:

  • Method is not synchronized
  • The c.ize () method in while(true) is never detected because thread to thread is invisible.

Volatile was added to solve the problem

Program 2

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: blessed * 2020-07-19@Description: * /
public class T02_withVolatile {
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T02_withVolatile c = new T02_withVolatile();
        new Thread(()->{
            for (int i=0; i<10; i++){ c.add(new Object());
                System.out.println("add "+i);
                /*try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } * /}},"t1").start();
        new Thread(()->{
            while (true) {if(c.size()==5) {break;
                }
            }
            System.out.println("T2 end");
        },"t2").start(); }}Copy the code

Use volatile to modify the List collection, allowing information to be passed between threads. Running results:

  • Volatile modifies the reference type. The reference object refers to another new object that cannot be observed if the value of its member variable changes.

Application of 3

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the departed 2020-07-19 *@Description: * /
public class T03_NotifyHoldingLock {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(o);
    }

    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T03_NotifyHoldingLock c = new T03_NotifyHoldingLock();
        final Object lock = new Object();
        // Start t2 and then T1
        new Thread(()->{
            synchronized (lock){
                System.out.println("T2 start");
                if(c.size()! =5) {try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("T2 end"); }},"t2").start();
        new Thread(()->{
            System.out.println("T1 start");
            synchronized (lock){
                for (int i=0; i<10; i++){ c.add(new Object());
                    System.out.println("add "+i);
                    
                    if(c.size() == 5){
                        lock.notify();
                    }
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}},"t1").start(); }}Copy the code

Use synchronized locks (wait() and notify()) by locking an object and calling the wait() and notify() methods. Running results:

  • The notify() method does not release the lock. When the notify() method is called by the T1 thread, the current lock is not released. Therefore, the T1 thread will continue to execute.

Program 4:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the 2020-07-19 16:57 *@Description: * /
public class T04_NotifyHoldingLock {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(o);
    }

    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T04_NotifyHoldingLock c = new T04_NotifyHoldingLock();
        final Object lock = new Object();
        // Start t2 and then T1
        new Thread(()->{
            synchronized (lock){
                System.out.println("T2 start");
                if(c.size()! =5) {try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("T2 end");
            }
            // Tell T1 to continue execution
            lock.notify();
        },"t2").start();
        new Thread(()->{
            System.out.println("T1 start");
            synchronized (lock){
                for (int i=0; i<10; i++){ c.add(new Object());
                    System.out.println("add "+i);
                    if(c.size() == 5){
                        lock.notify();
                        // Release the lock to allow t2 to execute
                        try {
                            lock.wait();
                        } catch(InterruptedException e) { e.printStackTrace(); }}try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}},"t1").start(); }}Copy the code

Program 4 made some minor changes on the basis of program 3. It called wait() method to block T1 thread, release the lock, let T2 execute, and realize the real-time monitoring of T2. Running results:

We consider using other synchronization techniques to implement this problem: Procedure 5:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the 2020-07-19 17:06 *@Description: * /
public class T05_CountDownLatch {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T05_CountDownLatch c = new T05_CountDownLatch();
        // Start t2 and then T1
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(()->{
            System.out.println("T2 start");
            if(c.size()! =5) {try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("T2 end");
        },"t2").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            System.out.println("T1 start");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size()==5) {// Suspend the T1 threadlatch.countDown(); }}},"t1").start(); }}Copy the code

This is done using CountDownLatch, and unlike program 4, without the lock, replace the WAIT () methods of t2 and T1 threads with await() methods. Running results:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: * he was 2020-07-19@Description: * /
public class T06_CountDownLatch {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(a){
        return list.size();
    }

    public static void main(String[] args) {
        T06_CountDownLatch c = new T06_CountDownLatch();
        // Start t2 and then T1
        CountDownLatch latch = new CountDownLatch(1);
        new Thread(()->{
            System.out.println("T2 start");
            if(c.size()! =5) {try {
                    latch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("T2 end");
        },"t2").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            System.out.println("T1 start");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size()==5) {// Suspend the T1 thread
                    latch.countDown();
                    try {
                        latch.await();
                    } catch(InterruptedException e) { e.printStackTrace(); }}try {
                    TimeUnit.SECONDS.sleep(1);
                } catch(InterruptedException e) { e.printStackTrace(); }}},"t1").start(); }}Copy the code

Program 6 on the basis of program 5, when T1 thread opens the latch of T2 thread, add a latch to T1 itself.

Program 7:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the 2020-07-19 returned to *@Description: * /
public class T07_LockSupport {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(a){
        return list.size();
    }

    static Thread t1=null,t2=null;
    public static void main(String[] args) {
        T07_LockSupport c = new T07_LockSupport();
        CountDownLatch latch = new CountDownLatch(1);
        
        t2=new Thread(()->{
            System.out.println("T2 start");
            if(c.size()! =5){
                LockSupport.park();
            }
            System.out.println("T2 end");
            LockSupport.unpark(t1);
        },"t2");
        

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t1=new Thread(()->{
            System.out.println("T1 start");
            for (int i = 0; i < 10; i++) {
                c.add(new Object());
                System.out.println("add "+i);
                if(c.size() ==5){
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch(InterruptedException e) { e.printStackTrace(); }}},"t1");
        // Start t2 and then T1t2.start(); t1.start(); }}Copy the code

The implementation principle is the same as program 5, but the use of LockSupport’s park() method and unpark() method to achieve.

Program 8:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/ * * *@BelongsProject: test22
 * @BelongsPackage: PACKAGE_NAME
 * @Author: Gao Chuanwei *@CreateTime: the slew both 2020-07-19 *@Description: * /
public class T08_Semaphore {
    // Add volatile so that T2 can be notified
    volatile List list = new ArrayList();

    public void add(Object o){
        list.add(0);
    }

    public int size(a){
        return list.size();
    }

    static Thread t1=null,t2=null;
    public static void main(String[] args) {
        T08_Semaphore c = new T08_Semaphore();
        Semaphore s = new Semaphore(1);
        t1=new Thread(()->{
            try {
                s.acquire();
                for (int i = 0; i < 5; i++) {
                    c.add(new Object());
                    System.out.println("add "+i);
                }
                s.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                t2.start();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                s.acquire();
                for (int i = 5; i < 10; i++) {
                    c.add(new Object());
                    System.out.println("add "+i);
                }
                s.release();
            } catch(InterruptedException e) { e.printStackTrace(); }},"t1");

        t2=new Thread(()->{
            try {
                s.acquire();
                System.out.println("T2 end");
                s.release();
            } catch(InterruptedException e) { e.printStackTrace(); }},"t2"); t1.start(); }}Copy the code

Around this problem, a total of five technologies are used to achieve:

  • volatile
  • Wait () and notify ()
  • CountDownLatch
  • LockSupport
  • Semaphore