Several ways to implement communication between threads

Several ways to implement communication between threads

1. Traditional thread communication synchronized + wait + notify

The wait (), notify () and notifyAll () methods of the Object class must be called by the synchronous monitor object. There are two cases:

a) Synchronization method, the default instance of this class (this) is the synchronization monitor, which can be called directly in the synchronization method

b) The synchronization code block, the synchronization monitor is the object in the brackets after being synchronized, so you must use this object to call these three methods

 

Second, use Condition to control thread communication lock + condition + await + signal

Lock replaces the synchronization method or synchronization code block, and Condition replaces the function of the synchronization monitor.

private final Lock lock = newReentrantLock();

private final Condition con =lock.newCondition();

lock.lock();   con.await();    con.signalAll();    lock.unlock():

 

Three, use blocking queue (BlockingQueue) to control thread communication

The BlockingQueue interface is mainly used as a tool for thread synchronization. When the producer tries to put an element into the BlockingQueue, the thread is blocked if the queue is full; when the consumer tries to get the element into the BlockingQueue, the thread is blocked if the queue is empty.

 

 

Several implementations of inter-thread communication
First, we must understand that there are two models of inter-thread communication : shared memory and message passing. The following methods are basically implemented by these two models. Let's analyze a common question in an interview:
1
Topic: There are two threads A and B. The A thread adds the element "abc" string to a set in sequence, adding a total of ten times, when added to the fifth time It is hoped that the B thread can receive the notification of the A thread, and then the B thread performs related business operations.

Method 1: Use the volatile keyword
to realize mutual communication between threads based on the volatile keyword is the idea of ​​using shared memory, which roughly means that multiple threads monitor a variable at the same time. When this variable changes, the thread can perceive and execute the corresponding business. This is also the simplest way to achieve

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中添加一个元素,此时list中的元素个数为:" + 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();
    }
}



The running result is:


Method 2: Use the wait () and notify () methods
of the Object class. As we all know, the Object class provides methods for inter-thread communication: wait (), notify (), notifyaAl (), which are the basis of multi-thread communication. The idea of ​​implementation is naturally inter-thread communication.

Note: wait and notify must be used with synchronized, the wait method releases the lock, the notify method does not release the lock

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中添加一个元素,此时list中的元素个数为:" + 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();
    }
}


The result is


It can be seen from the screenshot of the print result that after thread A issues the notify () wake-up notification, thread B only starts executing after running its own thread's business. This also shows that the notify () method does not release the lock, and wait ( ) Method to release the lock.

Method 3: After using the JUC tool class CountDownLatch
jdk1.5, many concurrent programming related tool classes are provided under the java.util.concurrent package, which simplifies the writing of our concurrent programming code. *** CountDownLatch *** is based on the AQS framework , 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();
    }
}



The running result is:


Use ReentrantLock with Condition

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中添加一个元素,此时list中的元素个数为:" + 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();
    }
}



The running result is:

Obviously, this method is not very good to use, the code writing is complicated, and thread B cannot be executed immediately after it is awakened by A because it does not acquire the lock. This method is the same as wait () and notify () of Object.

Method 5: Basic LockSupport for inter-thread blocking and wake-up
LockSupport is a very flexible tool for inter-thread blocking and wake-up. Use it without paying attention to whether to wait for the thread to start or wake the thread to run 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中添加一个元素,此时list中的元素个数为:" + list.size());
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (list.size() == 5)
                    LockSupport.unpark(threadB);
            }
        });
        threadA.start();
        threadB.start();
    }
}



operation result

 

Published 13 original articles · Like 3 · Visits 4986

Guess you like

Origin blog.csdn.net/u010919402/article/details/105446136