• 三个线程交叉打印ABC(synchronized、wait/notify)


    三个线程交叉打印ABC(synchronized、wait/notify)

      这是以前在面试美团Java实习生岗位时被问到的一道题,当时没有写出来,后面在网上看别人代码也是云里雾里,感觉每个人写的也都不一样,没有强调重点,导致看的越多就感觉越乱,有人用一把锁,也有人用三把锁。这次我尽力把交叉打印讲清楚,把重点都标注出来。

      首先,线程打印字母是线程在执行一个任务,所以我们需要定义三个任务(Runnable),然后交给三个线程去执行(Thread)。

      我们要明白三个线程交叉打印,这三个线程之间时需要相互通信的,也就是说线程1打印完A之后,需要通知线程2去打印B,线程2打印完B之后,需要通知线程3去打印C,以此类推。好了,我们现在知道线程之间是要相互通信的,那么线程之间要怎么通信呢?可共享变量是Java给我提供的天然的通信媒介,线程可以通过读取共享变量的内容,然后线程本身来决定在线程中进行什么操作。

      这个共享变量就是作为锁对象,只有获取到了锁对象才可以执行任务中的逻辑,否则任务会被阻塞。

      由于代码在执行的过程中会出现原子性、可见性、指令重排等情况,所以Java提供了如synchronized关键字来保证线程通信时的正确性。如果不清楚原子性、可见性、指令重排也没有关系,只要清楚synchronized可以保证线程之间在通信时的正确性就行了,synchronized的作用就是加锁。

      wait/notify都是对于锁对象的操作,不是执行任务的线程来进行wait/notify,这一点非常重要!!!锁对象的作用是持有锁对象的线程才可以进行打印,如果持有锁对象的线程不满足打印条件(条件在代码中有说明),那么就需要让出锁对象,让出锁对象的操作就是wait,然后通知其他线程来竞争锁对象,通知的操作就是notify,记住wait/notify都是由锁对象来执行的!!!

      好了,我们现在明白我们需要干什么了:定义三个任务,然后交给三个线程去执行、使用一个共享变量来在线程中进行通信、用synchronized来保证线程通信时的正确性

      接下来,我们来看代码:

    public class Test07 {
    
        //定义一个共享变量,用来在线程中进行通信
        private static final Object obj = new Object();
    
        //定义一个变量来记录打印的次数,控制打印条件
        private static int count = 1;
    
        public static void main(String[] args) {
            //创建三个线程,然后把三个任务分别放入这三个线程执行
    
            //创建线程1执行任务1
            new Thread(new Task1()).start();
            //创建线程2执行任务2
            new Thread(new Task2()).start();
            //创建线程3执行任务4
            new Thread(new Task3()).start();
        }
    
        //任务1
        private static class Task1 implements Runnable {
    
            @Override
            public void run() {
                synchronized (obj) {
                    //打印十次A
                    for (int i = 0; i < 10; i++) {
                        //一直轮询,如果条件不是要打印A的条件,那么直接释放锁
                        /*
                            这个地方一定要用while,如果用了if的话,再下次重新获得锁的时候,是继续往下去执行,走到obj.notifyAll()这一行代码,不会再判断count的值,
                            用while的话会再去判断count的值,如果符合条件再往下执行,走到obj.notifyAll()这一行代码,可以自己把while换成if试试,看看结果
                         */
                        while (count % 3 != 1) {
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //通知其他所有线程可以来抢占锁了,但是发现现在线程1在持有锁,其他线程还抢不到,只有等到线程1释放锁之后,才可以抢到锁
                        obj.notifyAll();
                        System.out.println("A");
                        count++;
                    }
                }
            }
        }
    
        //任务2
        private static class Task2 implements Runnable {
    
            @Override
            public void run() {
                synchronized (obj) {
                    //打印十次B
                    for (int i = 0; i < 10; i++) {
                        //一直轮询,如果条件不是要打印B的条件,那么直接释放锁
                        while (count % 3 != 2) {
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //通知其他所有线程可以来抢占锁了,但是发现现在线程2在持有锁,其他线程还抢不到,只有等到线程2释放锁之后,才可以抢到锁
                        obj.notifyAll();
                        System.out.println("B");
                        count++;
                    }
                }
            }
        }
    
        //任务3
        private static class Task3 implements Runnable {
    
            @Override
            public void run() {
                synchronized (obj) {
                    //打印十次C
                    for (int i = 0; i < 10; i++) {
                        //一直轮询,如果条件不是要打印C的条件,那么直接释放锁
                        while (count % 3 != 0) {
                            try {
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //通知其他所有线程可以来抢占锁了,但是发现现在线程3在持有锁,其他线程还抢不到,只有等到线程3释放锁之后,才可以抢到锁
                        obj.notifyAll();
                        System.out.println("C");
                        count++;
                    }
                }
            }
        }
    }
    
  • 相关阅读:
    如何编辑SDE数据(转)
    常用sql语句
    JavaScript 实现地图打印
    什么是3G通信?
    一种客户端得到后台某个值的方法
    如何利用C#创建和调用DLL(转)
    C#中如何调用动态链接库DLL(转)
    一个ADF Javascript 添加鼠标移动事件的例子
    ArcGIS Server网站发布后地图显示空白的原因
    硬盘录像机监听按钮不起作用
  • 原文地址:https://www.cnblogs.com/rao11/p/13728941.html
Copyright © 2020-2023  润新知