[Multi-threaded advanced] How to ensure the order of singing and dancing rap and playing basketball

insert image description here

Foreword
Abba, Abba Aba Aba Aba Aba, Aba, Aba Aba

I'm afraid you won't be able to learn it, so I spent a few more hairs to come up with this demo


There is such a demand recently (real scene)

I opened 10 threads to send messages to users at the same time, once every 10 minutes. Among them, 9 business threads are responsible for sending messages , and the remaining one is used to obtain the latest message strategy (the so-called strategy is to specify which users to send; Through which channels to send, WeChat, email, SMS, etc.), the reason for each acquisition is that the strategy may change at any time. I designed it so that every time I get the latest one, I can flexibly change the strategy without restarting the project (the strategy exists in the database middle)

Here comes the problem

Each business thread needs to use the latest strategy for sending messages, so the chores thread must be executed first, and the thread scheduling is random, and the execution order is determined by the operating system. How can I make the service thread execute after the chorus thread is finished?

This involves communication between threads. The purpose of this chestnut is to give everyone a general understanding of the application of communication between threads in actual projects.

This scene is real, but it doesn't matter if you don't understand it

Friends who are familiar with me know that bloggers are warm men. Of course, the chestnuts they hold will not be so boring.

so

Today's protagonist is: Ah Ji

insert image description here
I'm sorry chicken, I have to do this

Story background: Ah Ji needs to learn to sing and dance rap first, and then start to learn to play basketball

Translated into multi-threading: one thread is responsible for learning to sing and dance rap, and the other thread is responsible for learning to play basketball, and the scheduling of these two threads is random, and the order is determined by the operating system, how can we ensure that Ah Ji learns sing and dance rap, and then learn play basketball

There are many methods. I will list a few commonly used ones here, which are enough to deal with all scenarios.

  • join based
  • based on volatile
  • Based on synchronized
  • Based on reentrantLock
  • Based on countDownLatch

Don't push too hard, be talented

Let's get started first: join

join is a method of the Thread class, the bottom layer is based on wait+notify, you can understand this method as a queue cut, whoever calls will cut the queue, but there are limitations

It is suitable for scenarios with fewer threads. If there are too many threads, it will cause infinite nesting dolls, which is a bit troublesome and not elegant.

public class JoinTest {
    
    
    // 用来记录啊鸡学习时间
    static double year;

    public static void main(String[] args) {
    
    
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
    
    
            for (year = 0.5; year <= 5; year += 0.5) {
    
    
                System.out.println("开始练习唱跳rap:已练习" + year + "年");
                try {
    
    
                    Thread.sleep(288);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                //众所周知,练习两年半即可出道
                if (year == 2.5) {
    
    
                    System.out.println("===========================>练习时长两年半,出道!!!");
                    //留意下这个break,想想如果不break会怎样
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
    
    
            try {
    
    
            	// 让threadA线程插队,threadB执行到这儿时会被阻塞,直到threadA执行完
                threadA.join();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("开始练习打篮球");
        });
        // 启动线程
        threadA.start();
        threadB.start();
    }
}

insert image description here

No matter how many times you run it, the result is the same, and it's the same today when Jesus comes, I said

If you don't break, it will naturally wait for threadA to finish executing threadB before starting to execute.
insert image description here

via volatile

This implementation is relatively simple and easy to understand, but the performance is not good, and it will seize a lot of CPU resources. If it is not necessary, do not use it.

public class VolatileTest {
    
    
    //定义一个共享变量用来线程间通信,注意用volatile修饰,保证它内存可见
    static volatile boolean flag = false;
    static double year;

    public static void main(String[] args) {
    
    
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
    
    
            while (true) {
    
    
                if (!flag) {
    
    
                    for (year = 0.5; year <= 5; year += 0.5) {
    
    
                        System.out.println("开始练习唱跳rap:已练习" + year + "年");
                        try {
    
    
                            Thread.sleep(288);
                        } catch (InterruptedException e) {
    
    
                            e.printStackTrace();
                        }
                        //众所周知,练习两年半即可出道
                        if (year == 2.5) {
    
    
                            System.out.println("===========================>练习时长两年半,出道!!!");
                            // 通知threadB你可以执行了
                            flag = true;
                            //同样留意这个break
                            break;
                        }
                    }
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
    
    
            while (true) {
    
    
            	// 监听flag
                if (flag) {
    
    
                    System.out.println("开始练习打篮球");
                    break;
                }
            }
        });
        threadA.start();
        threadB.start();
    }
}

The result is the same as the first one above, so I won't show it.
About break, I won't talk about it here. Guess the result first, and then copy the demo to run. You must do it.

Synchronized, wait(), notify() three-piece set

Both wait() and notify() are communication methods of the Object class. Note that wait and notify need to be used with synchronized. Note that notify will not release the lock. As for where the lock will not be released, the demo is explained below.

public class SynchronizedTest {
    
    
    static double year;

    public static void main(String[] args) {
    
    
        SynchronizedTest sync= new SynchronizedTest();
        sync.execute();
    }

    public void execute() {
    
    
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
    
    
            synchronized (this) {
    
    
                for (year = 0.5; year <= 5; year += 0.5) {
    
    
                    try {
    
    
                        System.out.println("开始练习唱跳rap:已练习" + year + "年");
                        Thread.sleep(288);
                        if (year == 2.5) {
    
    
                            System.out.println("===========================>练习时长两年半,出道!!!");
                            //唤醒等待中的threadB,但threadB不会立马执行,而是等待threadA执行完,因为notify不会释放锁
                            notify();
                            break;
                        }
                    } catch (InterruptedException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
    
    
            synchronized (this) {
    
    
                try {
    
    
                    wait();
                    System.out.println("开始练习打篮球");
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
            }
        });
        //注意,一定要先启动B,不然会导致B永远阻塞
        threadB.start();
        threadA.start();
    }
}

You must think more about the break in threadA. You will know what it means to not release the lock after running it
. If there is no break, threadA will continue to execute its own logic after waking up threadB, and will release the lock when it is finished executing. When threadB starts to execute

Based on ReentrantLock

ReentrantLock is a concurrency tool under the juc package. It can also be implemented, but it is relatively complicated. It needs to be combined with the await and signal of the Condition. The underlying principle is a bit like the wait and notify above.

Here is an after-school homework: think about why unlock should be placed in finally?

public class ReentrantLockTest {
    
    
    static double year;

    public static void main(String[] args) {
    
    
    	//实例化一个锁和Condition
        ReentrantLock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
    
    
            lock.lock();
            try {
    
    
                for (year = 0.5; year <= 5; year += 0.5) {
    
    
                    System.out.println("开始练习唱跳rap:已练习" + year + "年");
                    Thread.sleep(288);
                    //众所周知,练习两年半即可出道
                    if (year == 2.5) {
    
    
                        System.out.println("===========================>练习时长两年半,出道!!!");
                        //唤醒等待中的线程
                        condition.signal();
                        //这里的break也是个彩蛋,去掉它触发隐藏关卡
                        break;
                    }
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                //解锁
                lock.unlock();
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
    
    
            lock.lock();
            try {
    
    
                //让当前线程等待
                condition.await();
                System.out.println("开始练习打篮球");
            } catch (Exception e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                lock.unlock();
            }
        });
        //必须保证B先拿到锁,不然会导致A永远阻塞
        threadB.start();
        threadA.start();
    }
}

Based on CountDownLatch

This is also a concurrency tool under the juc package. There are two common methods. countDown and
await If count is already 0 before decrementing by one, then nothing will happen. If it becomes 0 after decrementing by one, all waiting threads will be woken up; the await method will make the current thread wait until count is 0

public class CountDownLatchTest {
    
    
    static double year;

    public static void main(String[] args) {
    
    
    	//实例化一个CountDownLatch,count设置为1,也就是说,只要调用一次countDown方法就会唤醒线程
        CountDownLatch latch = new CountDownLatch(1);
        //线程A,练习唱跳rap
        Thread threadA = new Thread(() -> {
    
    
            for (year = 0.5; year <= 5; year += 0.5) {
    
    
                System.out.println("开始练习唱跳rap:已练习" + year + "年");
                try {
    
    
                    Thread.sleep(288);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                //众所周知,练习两年半即可出道
                if (year == 2.5) {
    
    
                    System.out.println("===========================>练习时长两年半,出道!!!");
                    //计数器减一
                    latch.countDown();
                    //老规矩,去掉break触发隐藏关卡
                    break;
                }
            }
        });
        //线程B,练习打篮球
        Thread threadB = new Thread(() -> {
    
    
            try {
    
    
                //阻塞当前线程,计数器为0时被唤醒
                latch.await();
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("开始练习打篮球");
        });
        threadA.start();
        threadB.start();
    }
}

finish work

Master Luo Town Building
insert image description here

If you understand the above five demos, you are familiar with multithreading, congratulations! ! !

There are breaks in threadA of the demo, which is specially designed by me. Observe the running results with and without break. I believe you will gain a lot.


ok i'm done

Guess you like

Origin blog.csdn.net/qq_33709582/article/details/121900989