Several ways to realize communication between threads

Overview of thread communication

There are two models of inter-thread communication: shared memory and message passing. The following are all implemented around these two

Ask a question

There are two threads A and B. The B thread adds the element "abc" string to a collection in sequence, adding 10 times in total. When it is added to the fifth time, it is hoped that thread A can receive the notification of thread B, and then B Threads perform related business operations

Method 1: Use the wait() and notify() methods of the Object class

  • The Object class provides methods for inter-thread communication: wait(), notify(), and notifyAll(), which are the basis of multi-thread communication, and the idea of ​​this implementation is naturally inter-thread communication.
    Thread A waits for a certain condition to be met (list.size()==5) before performing the operation. Thread B adds elements to the list and changes the size of the list.

  • How to communicate between A and B? In other words, how does thread A know that list.size() is already 5?

    • The wait() and notify() methods of the Object class are used here.

    When the condition is not met (list.size() !=5), thread A calls wait() to give up the CPU and enter the blocked state.

    When the condition is met, thread B calls notify() to notify thread A. The so-called notification thread A is to wake up thread A and let it enter the runnable state.

    One of the benefits of this approach is that the utilization of the CPU is increased.

    But there are also some disadvantages: For example, thread B executes first, adds 5 elements at once and calls notify() to send a notification, while thread A is still executing; when thread A executes and calls wait(), then it It will never be possible to wake up. Because, thread B has already sent a notification, and will no longer send notifications in the future. This shows that if the notification is too early, the execution logic of the program will be disrupted.

public class TestSync {
    
    
    public static void main(String[] args) {
    
    
        //定义一个锁对象
        Object lock = new Object();
        List<String>  list = new ArrayList<>();
        // 线程A
        Thread threadA = new Thread(() -> {
    
    
            synchronized (lock) {
    
    
                for (int i = 1; i <= 10; i++) {
    
    
                    list.add("abc");
                    System.out.println("线程A添加元素,此时list的size为:" + list.size());
                    try {
    
    
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                    if (list.size() == 5)
                        lock.notify();//唤醒B线程
                }
            }
        });
        //线程B
        Thread threadB = new Thread(() -> {
    
    
            while (true) {
    
    
                synchronized (lock) {
    
    
                    if (list.size() != 5) {
    
    
                        try {
    
    
                            lock.wait();
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                }
            }
        });
        //需要先启动线程B
        threadB.start();
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        //再启动线程A
        threadA.start();
    }
}

Please add a picture description

Method 2: The newContition() method in the Lock interface returns a Condition object, and the Condition class can also implement the wait/notify mode

public class TestSync {
    
    
    public static void main(String[] args) {
    
    
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        List<String> list = new ArrayList<>();
        //线程A
        Thread threadA = new Thread(() -> {
    
    
            lock.lock();
            for (int i = 1; i <= 10; i++) {
    
    
                list.add("abc");
                System.out.println("线程A添加元素,此时list的size为:" + list.size());
                try {
    
    
                    Thread.sleep(500);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    condition.signal();
            }
            lock.unlock();
        });
        //线程B
        Thread threadB = new Thread(() -> {
    
    
            lock.lock();
            if (list.size() != 5) {
    
    
                try {
    
    
                    condition.await();
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
            lock.unlock();
        });
        threadB.start();
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        threadA.start();
    }
}

Please add a picture description

Method 3: Use the volatile keyword

The idea of ​​using shared memory to implement inter-thread communication based on the volatile keyword basically means that multiple threads monitor a variable at the same time. When the variable changes, the thread can sense and execute the corresponding business. This is also the easiest way to implement

public class TestSync {
    
    
    //定义共享变量来实现通信,它需要volatile修饰,否则线程不能及时感知
    static volatile boolean notice = false;

    public static void main(String[] args) {
    
    
        List<String>  list = new ArrayList<>();
        //线程A
        Thread threadA = new Thread(() -> {
    
    
            for (int i = 1; i <= 10; i++) {
    
    
                list.add("abc");
                System.out.println("线程A添加元素,此时list的size为:" + list.size());
                try {
    
    
                    Thread.sleep(500);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    notice = true;
            }
        });
        //线程B
        Thread threadB = new Thread(() -> {
    
    
            while (true) {
    
    
                if (notice) {
    
    
                    System.out.println("线程B收到通知,开始执行自己的业务...");
                    break;
                }
            }
        });
        //需要先启动线程B
        threadB.start();
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

Please add a picture description

Method 4: Basic LockSupport implements blocking and waking up between threads

LockSupport is a very flexible tool for inter-thread blocking and wakeup.
When using it, you don't need to pay attention to whether the waiting thread runs first or the wake-up thread runs first, but you need to know the name of the thread.

public class TestSync {
    
    
    public static void main(String[] args) {
    
    
        List<String> list = new ArrayList<>();
        //线程B
        final Thread threadB = new Thread(() -> {
    
    
            if (list.size() != 5) {
    
    
                LockSupport.park();
            }
            System.out.println("线程B收到通知,开始执行自己的业务...");
        });
        //线程A
        Thread threadA = new Thread(() -> {
    
    
            for (int i = 1; i <= 10; i++) {
    
    
                list.add("abc");
                System.out.println("线程A添加元素,此时list的size为:" + list.size());
                try {
    
    
                    Thread.sleep(500);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    LockSupport.unpark(threadB);
            }
        });
        threadA.start();
        threadB.start();
    }
}

Method 5: Use the JUC tool class CountDownLatch

After jdk1.5, a lot of tools related to concurrent programming are provided under the java.util.concurrent package, which simplifies the writing of our concurrent programming code. CountDownLatch is based on the AQS framework, which is equivalent to maintaining a shared variable state between threads

public class TestSync {
    
    
    public static void main(String[] args) {
    
    
        CountDownLatch countDownLatch = new CountDownLatch(1);
        List<String>  list = new ArrayList<>();
        // 实现线程A
        Thread threadA = new Thread(() -> {
    
    
            for (int i = 1; i <= 10; i++) {
    
    
                list.add("abc");
                System.out.println("线程A向list中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
    
    
                    Thread.sleep(500);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    countDownLatch.countDown();
            }
        });
        // 实现线程B
        Thread threadB = new Thread(() -> {
    
    
            while (true) {
    
    
                if (list.size() != 5) {
    
    
                    try {
    
    
                        countDownLatch.await();
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
                System.out.println("线程B收到通知,开始执行自己的业务...");
                break;
            }
        });
        // 需要先启动线程B
        threadB.start();
        try {
    
    
            Thread.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        // 再启动线程A
        threadA.start();
    }
}

Guess you like

Origin blog.csdn.net/weixin_54046648/article/details/128173435