线程间常见通信方式

面试题

线程间通信的方式有哪些(有两个线程A、B,A线程向一个集合里面依次添加元素"abc"字符串,一共添加十次,当添加到第五次的时候,希望B线程能够收到A线程的通知,然后B线程执行相关的业务操作)

简答

  • 通信模型

    线程间通信的模型有两种:共享内存和消息传递

线程

共享内存

使用 volatile 关键字

  • 简介

    volatile修饰的变量值直接存在主内存里面,子线程对该变量的读写直接写入主内存,而不是像其它变量一样在local thread里面产生一份copy。volatile能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。

  • 案例代码

    
        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();
            }
        }
    
    
    
    
  • 输出结果

    
    
        线程A向list中添加一个元素,此时list中的元素个数为:1
        线程A向list中添加一个元素,此时list中的元素个数为:2
        线程A向list中添加一个元素,此时list中的元素个数为:3
        线程A向list中添加一个元素,此时list中的元素个数为:4
        线程A向list中添加一个元素,此时list中的元素个数为:5
        线程A向list中添加一个元素,此时list中的元素个数为:6
        线程B收到通知,开始执行自己的业务...
        线程A向list中添加一个元素,此时list中的元素个数为:7
        线程A向list中添加一个元素,此时list中的元素个数为:8
        线程A向list中添加一个元素,此时list中的元素个数为:9
        线程A向list中添加一个元素,此时list中的元素个数为:10
    
    
    
    

使用Object类的Synchronized、wait、notify

  • 简介

    wait和notify必须配合synchronized使用,wait方法释放锁,notify方法不释放锁

  • API介绍

    https://www.cnblogs.com/jinhengyu/p/10257830.html

    1. void notify()

    Wakes up a single thread that is waiting on this object’s monitor.
    译:唤醒在此对象监视器上等待的单个线程

    1. void wait()

    Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
    译:导致当前的线程等待,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法

  • 测试API代码

    
        //按理说,当第一个线程(前面)的synchronized获得对象锁了以后,第二个线程(后面)就只能等待,当第一个线程添加完list,执行demo.list.notify()方法只会通知等待队列中的第一个相关线程,根据输出结果看是去唤醒第二个线程,这个时候第二个线程执行添加list。问题是:但是notify()方法不会释放锁,且第一个线程先获取到锁,那么第二个线程是应该是无法先获取到锁的,是如何进入wait()方法,等待第一个线程获取的
    
        //问题:那么第二个线程是什么时候获取到demo.list对象锁的,进入等待的
        public class ThreadCommunication {
    
    
                private final List<Integer> list =new ArrayList<>();
    
                public static void main(String[] args) {
                    ThreadCommunication demo =new ThreadCommunication();
    
                    new Thread(()->{
                        for (int i=0;i<10;i++){
                            synchronized (demo.list){
                                if(demo.list.size()%2==1){
                                    try {
                                        demo.list.wait();
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                demo.list.add(i);
                                System.out.print(Thread.currentThread().getName());
                                System.out.println(demo.list);
                                demo.list.notify();//不会释放锁
                            }
                        }
    
                    }).start();
    
                    new Thread(()->{
                        for (int i=0;i<10;i++){
                            synchronized (demo.list){
                                if(demo.list.size()%2==0){
                                    try {
                                        demo.list.wait();
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                                demo.list.add(i);
                                System.out.print(Thread.currentThread().getName());
                                System.out.println(demo.list);
                                demo.list.notify();
                            }
                        }
                    }).start();
                }
    
    
        }
    
    
    
    
  • 总结

    1. wait(),notify(),notifyAll()都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait(),notify(),notifyAll() 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了

    2. 当需要调用以上的方法的时候,一定要对竞争资源进行加锁,如果不加锁的话,则会报 IllegalMonitorStateException 异常

    3. 想要调用wait()进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中

    4. 在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知

    5. 调用obj.wait()释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。

    6. notify()方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)

    7. notifyAll()通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)

    8. 假设有三个线程执行了obj.wait(),那么obj.notifyAll()则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行

    9. 调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行。

  • 案例代码

    
        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();
            }
        }
    
    
    
    
    
  • 打印结果

    
        线程A向list中添加一个元素,此时list中的元素个数为:1
        线程A向list中添加一个元素,此时list中的元素个数为:2
        线程A向list中添加一个元素,此时list中的元素个数为:3
        线程A向list中添加一个元素,此时list中的元素个数为:4
        线程A向list中添加一个元素,此时list中的元素个数为:5
        线程A向list中添加一个元素,此时list中的元素个数为:6
        线程A向list中添加一个元素,此时list中的元素个数为:7
        线程A向list中添加一个元素,此时list中的元素个数为:8
        线程A向list中添加一个元素,此时list中的元素个数为:9
        线程A向list中添加一个元素,此时list中的元素个数为:10
        线程B收到通知,开始执行自己的业务...
    
    
    
    
  • 结果分析

    在线程A发出notify()唤醒通知之后,依然是走完了自己线程的业务之后,线程B才开始执行,这也正好说明了,notify()方法不释放锁,而wait()方法释放锁,执行后,线程B会一直在等待

JUC工具类CountDownLatch(闭锁)

  • 简介

    jdk1.5之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了我们的并发编程代码的书写,***CountDownLatch***基于AQS框架,相当于也是维护了一个线程间共享变量

    CyclicBarrier与CountDownLatch恰恰相反,是做加法到指定阀值,而CountDownLatch是做减法到0

  • API介绍

    1. void countDown()
      减少锁存器的计数,如果计数达到零,释放所有等待的线程

    2. void await()
      导致当前线程阻塞等到锁存器计数到零,除非线程是 interrupted

  • 案例代码

    
        //枚举类(开发技巧)
        public enum CountryEnum {
    
            ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"韩"),FIVE(5,"赵"),SIX(6,"魏");
    
            private Integer retCode;
    
            private String retMessage;
    
            CountryEnum(Integer retCode, String retMessage) {
                this.retCode = retCode;
                this.retMessage = retMessage;
            }
    
            public Integer getRetCode() {
                return retCode;
            }
    
            public String getRetMessage() {
                return retMessage;
            }
    
            /**
            * @Author charlesYan
            * @Description //遍历枚举值,返回对应枚举对象
            * @Date 15:02 2020/5/27
            * @Param [index]
            * @return com.neighbor.nbsp.multiThread.CountryEnum
            **/
            public static CountryEnum foreachCountryEnum(int index){
    
                CountryEnum[] values = CountryEnum.values();
                for (CountryEnum value : values) {
                    if(index == value.getRetCode()){
                        return value;
                    }
                }
                return null;
            }
        }
    
    
    
        //CountDownLatch进行线程间通信测试类
        public class CountDownLatchDemo {
    
            public static final int count = 6;
    
            public static void main(String[] args) throws InterruptedException {
    
                CountDownLatch countDownLatch = new CountDownLatch(count);
    
                for (int i = 1; i <= 6; i++) {
    
                    new Thread(() -> {
    
                        System.out.println(Thread.currentThread().getName() + " 被灭亡");
                        countDownLatch.countDown();//每次减少一
                    },CountryEnum.foreachCountryEnum(i).getRetMessage()).start();
                }
                countDownLatch.await();
                System.out.println(Thread.currentThread().getName() + " 秦一统天下...");
            }
    
        }
    
    
  • 打印结果

    
        齐 被灭亡
        楚 被灭亡
        燕 被灭亡
        韩 被灭亡
        赵 被灭亡
        魏 被灭亡
        main 秦一统天下...
    
    
  • 结果分析

    main主线程会一直阻塞到计数器为0时,才被唤醒执行,但其他线程不会保证执行顺序

使用ReentrantLock结合Condition

  • 简介

    ReentrantLock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像Synchronized要么随机唤醒单个线程,要么唤醒全部线程

    调用Condition的await(),当前线程会释放该条件对象关联的锁,但signal()不会释放锁

    在l上绑定锁:Condition c1 = l.newCondition(); 这样在l上就绑定了一把小锁。与synchronized不同的是,l可以绑定任意多把锁:Condition c2 = l.newCondition();

  • 业务需求

    多线程之间按顺序调用,实现A->B->C三个线程启动,要求:AA打印5次,BB打印10次,CC打印15次;紧接着AA打印5次,BB打印10次,CC打印15次…循环10轮

  • 案例代码

    
        /**
        * @ClassName ShareResource
        * @Description TODO 共享资源$
        * @Author charlesYan
        * @Date 2020/5/27 16:26
        * @Version 1.0
        **/
        public class ShareResource {
    
            private int number = 1;//A:1 B:2 C:3
            private Lock lock = new ReentrantLock();
    
            private Condition c1 = lock.newCondition();
            private Condition c2 = lock.newCondition();
            private Condition c3 = lock.newCondition();
    
            public void print5(){
                lock.lock();
    
                //1.避免线程虚假唤醒,使用while判断
                try {
                    while (number != 1){
                        c1.await();
                    }
    
                    //2.A线程打印5次
                    for (int i = 1; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + "\t" + i);
                    }
    
                    //3.通知B线程
                    number = 2;
                    c2.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
    
                } finally {
                    lock.unlock();
                }
    
            }
    
            /**
            * @Author charlesYan
            * @Description //打印10次
            * @Date 17:04 2020/5/27
            * @Param []
            * @return void
            **/
            public void print10(){
                lock.lock();
    
                //1.避免线程虚假唤醒,使用while判断
                try {
                    while (number != 2){
                        c2.await();
                    }
    
                    //2.B线程打印10次
                    for (int i = 1; i <= 10; i++) {
                        System.out.println(Thread.currentThread().getName() + "\t" + i);
                    }
    
                    //3.通知C线程
                    number = 3;
                    c3.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
    
                } finally {
                    lock.unlock();
                }
    
            }
    
    
            /**
            * @Author charlesYan
            * @Description //打印15次
            * @Date 17:04 2020/5/27
            * @Param []
            * @return void
            **/
            public void print15(){
                lock.lock();
    
                //1.避免线程虚假唤醒,使用while判断
                try {
                    while (number != 3){
                        c3.await();
                    }
    
                    //2.C线程打印15次
                    for (int i = 1; i <= 15; i++) {
                        System.out.println(Thread.currentThread().getName() + "\t" + i);
                    }
    
                    //3.通知A线程
                    number = 1;
                    c1.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
    
                } finally {
                    lock.unlock();
                }
    
            }
        }
    
    
    
    
        /**
        * @ClassName ReentrantLockDemo
        * @Description TODO 可重入锁测试$
        * @Author charlesYan
        * @Date 2020/5/27 17:07
        * @Version 1.0
        **/
        public class ReentrantLockDemo {
    
            public static void main(String[] args) {
    
                ShareResource shareResource = new ShareResource();
                new Thread(() -> {
                    for (int i = 0; i < 10; i++) {
                        shareResource.print5();
                    }
                },"A").start();
    
                new Thread(() -> {
                    for (int i = 0; i < 10; i++) {
                        shareResource.print10();
                    }
                },"B").start();
    
                new Thread(() -> {
                    for (int i = 0; i < 10; i++) {
                        shareResource.print15();
                    }
                },"C").start();
            }
        }
    
    
  • 打印结果

    
    
        A	1
        A	2
        A	3
        A	4
        A	5
        B	1
        B	2
        B	3
        B	4
        B	5
        B	6
        B	7
        B	8
        B	9
        B	10
        C	1
        C	2
        C	3
        C	4
        C	5
        C	6
        C	7
        C	8
        C	9
        C	10
        C	11
        C	12
        C	13
        C	14
        C	15
        ...循环十次
    
  • 结果分析

    1. 测试类代码,启动A、B、C三个线程,都是循环10次,其中A要求每次循环打印5次,B(10次),C(15次)

    2. 假设当B先获得对象锁,但是由于number = 1,所以B进入条件condition2(小锁)等待,此时会释放lock锁(C同理进入condition3等待)

    3. 只有A会跳过condition1,直接打印5次后,将number改成2,然后指定通知B线程,A释放锁后,B重新获得锁(此处不理解,print10方法是重新执行,还是说接着往下执行)

    4. B线程重新获得锁后,由于此时number = 2 ,执行打印10次,将number改成3,然后指定通知C线程

    5. C线程重新获得锁后,由于此时number = 3 ,执行打印15次,将number改成1,然后指定通知A线程。依次循环

利用AtomicInteger

  • volatile类似

消息传递

PipedInputStream\PipedOutputStream管道流

  • 简介

    流在两个线程间通信,但是Java中的Stream是单向的,所以在两个线程中分别建了一个input和output

  • 案例代码

    
        public class PipedDemo {
    
            private final PipedInputStream inputStream1;
            private final PipedOutputStream outputStream1;
            private final PipedInputStream inputStream2;
            private final PipedOutputStream outputStream2;
    
            public PipedDemo(){
                inputStream1 = new PipedInputStream();
                outputStream1 = new PipedOutputStream();
                inputStream2 = new PipedInputStream();
                outputStream2 = new PipedOutputStream();
                try {
                    inputStream1.connect(outputStream2);
                    inputStream2.connect(outputStream1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
            }
            /**程序退出时,需要关闭stream*/
            public void shutdown() throws IOException {
                inputStream1.close();
                inputStream2.close();
                outputStream1.close();
                outputStream2.close();
            }
    
    
            public static void main(String[] args) throws IOException {
                PipedDemo demo =new PipedDemo();
                new Thread(()->{
                    PipedInputStream in = demo.inputStream2;
                    PipedOutputStream out = demo.outputStream2;
    
                    for (int i = 0; i < 10; i++) {
                        try {
                            byte[] inArr = new byte[2];
                            in.read(inArr);
                            System.out.print(Thread.currentThread().getName()+": "+i+" ");
                            System.out.println(new String(inArr));
                            while(true){
                                if("go".equals(new String(inArr)))
                                    break;
                            }
                            out.write("ok".getBytes());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
    
                }).start();
    
                new Thread(()->{
                    PipedInputStream in = demo.inputStream1;
                    PipedOutputStream out = demo.outputStream1;
    
                    for (int i = 0; i < 10; i++) {
                        try {
                            out.write("go".getBytes());
                            byte[] inArr = new byte[2];
                            in.read(inArr);
                            System.out.print(Thread.currentThread().getName()+": "+i+" ");
                            System.out.println(new String(inArr));
                            while(true){
                                if("ok".equals(new String(inArr)))
                                    break;
                            }
    
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
    
                }).start();
        //        demo.shutdown();
            }
        }
    
    
    
    
    
  • 打印结果

    
        Thread-0: 0 go
        Thread-1: 0 ok
        Thread-0: 1 go
        Thread-1: 1 ok
        Thread-0: 2 go
        Thread-1: 2 ok
        Thread-0: 3 go
        Thread-1: 3 ok
        Thread-0: 4 go
        Thread-1: 4 ok
        Thread-0: 5 go
        Thread-1: 5 ok
        Thread-0: 6 go
        Thread-1: 6 ok
        Thread-0: 7 go
        Thread-1: 7 ok
        Thread-0: 8 go
        Thread-1: 8 ok
        Thread-0: 9 go
        Thread-1: 9 ok
    
    
    
    
    
  • 结果分析

利用BlockingQueue(阻塞队列)

  • 简介

    不需要关心何时需要阻塞线程,何时需要唤醒线程,因为这一切BlockingQueue都一手包办了

  • API介绍

    1. boolean add(E e)

      将指定的元素插入到此队列中,如果可以立即执行此操作而不违反容量限制, true在成功后返回,IllegalStateException如果当前没有可用空间,则抛出IllegalStateException

    2. boolean offer(Object)

      表示如果可能的话,将Object加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false。

    3. void put(E e)

      把Object加到BlockingQueue里,如果BlockingQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里有空间再继续。

    4. E poll(long timeout, TimeUnit unit)

      获取并删除BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null。当不传入time值时,立刻返回。

    5. E peek()

      立刻获取BlockingQueue里排在首位的对象,但不从队列里删除,如果队列为空,则返回null。

    6. E take()

      获取并删除BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的对象被加入为止。

  • 常用具体实现类

    1. ArrayBlockingQueue

      数组阻塞队列,规定大小,其构造函数必须带一个int参数来指明其大小。其所含的对象是以FIFO(先入先出)顺序排序的。

    2. LinkedBlockingQueue

      链阻塞队列,大小不定,若其构造函数带一个规定大小的参数,生成的BlockingQueue有大小限制,若不带大小参数,所生成的BlockingQueue的大小由Integer.MAX_VALUE来决定。其所含的对象是以FIFO顺序排序的。

    3. PriorityBlockingQueue

      类似于LinkedBlockingQueue,但其所含对象的排序不是FIFO,而是依据对象的自然排序顺序或者是构造函数所带的Comparator决定的顺序。

    4. SynchronousQueue

      特殊的BlockingQueue,它的内部同时只能够容纳单个元素,对其的操作必须是放和取交替完成的。

    5. DelayQueue

      延迟队列,注入其中的元素必须实现 java.util.concurrent.Delayed 接口

  • 业务需求

    模仿消息队列,每生产一个消息,消费一个消息

  • 案例代码

    
        //共享资源类
        class ShareData{
    
            private volatile boolean FLAG = true;//默认开启,进行生产+消费
            private AtomicInteger atomicInteger = new AtomicInteger();
    
            BlockingQueue<String> blockingQueue = null;
    
            public ShareData(BlockingQueue<String> blockingQueue) {
                this.blockingQueue = blockingQueue;
                System.out.println(blockingQueue.getClass().getName());
            }
    
            //生产者
            public void myProd() throws Exception {
                String data = null;
                boolean retValue;
    
                while(FLAG){
                    data = atomicInteger.incrementAndGet() + "";
                    retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);
                    if (retValue) {
                        System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "成功");
                    }else {
                        System.out.println(Thread.currentThread().getName() + "\t 插入队列" + data + "失败");
                    }
                    TimeUnit.SECONDS.sleep(1);
                }
                System.out.println(Thread.currentThread().getName() + "\t 手动停止,生产动作结束");
            }
    
            //消费者
            public void myConsumer() throws Exception {
    
                String result = null;
                while (FLAG){
    
                    result = blockingQueue.poll(2L,TimeUnit.SECONDS);
                    if (null == result || result.equals("")) {
                        FLAG = false;
                        System.out.println(Thread.currentThread().getName() + "\t 超过2秒没有取到,消费退出");
                        System.out.println("------");
                        System.out.println("------");
                        return;
                    }
                    System.out.println(Thread.currentThread().getName() + "\t 消费队列" + result + "成功");
                }
            }
    
            public void stop(){
                this.FLAG = false;
            }
        }
    
    
    
        public class BlockingQueueDemo {
    
            /**
            * @Author charlesYan
            * @Description //一个初始值为零的变量,两个线程对其交替操作,一个加1一个减1,来5轮
            *              1. 高内聚、低耦合,线程 操作 资源类
            *              2. 判断,干活,唤醒通知
            *              3. 防止多线程下的虚假唤醒 -- 使用while判断
            * @Date 9:47 2020/5/29
            * @Param [args]
            * @return void
            **/
            public static void main(String[] args) throws InterruptedException {
                ShareData shareData = new ShareData(new ArrayBlockingQueue<>(10));
    
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 生产线程启动");
    
                    try {
    
                        shareData.myProd();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                },"Producer").start();
    
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 消费线程启动");
                    try {
                        shareData.myConsumer();
                        System.out.println();
                        System.out.println();
    
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                },"Consumer").start();
    
                //暂停一会线程
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                System.out.println();
                System.out.println();
    
                System.out.println("5秒钟时间到,手动停止线程,活动结束");
                shareData.stop();
            }
        }
    
    
  • 打印结果

    
    
        java.util.concurrent.ArrayBlockingQueue
        Producer	 生产线程启动
        Consumer	 消费线程启动
        Producer	 插入队列1成功
        Consumer	 消费队列1成功
        Producer	 插入队列2成功
        Consumer	 消费队列2成功
        Producer	 插入队列3成功
        Consumer	 消费队列3成功
        Producer	 插入队列4成功
        Consumer	 消费队列4成功
        Producer	 插入队列5成功
        Consumer	 消费队列5成功
    
    
        5秒钟时间到,手动停止线程,活动结束
        Producer	 手动停止,生产动作结束
        Consumer	 超过2秒没有取到,消费退出
        ------
        ------
    
    
    
    
    
  • 结果分析

    这种方式,有时会出现打印顺序错乱的问题,如先消费,后插入,不清楚具体原因

参考链接

https://blog.csdn.net/wlddhj/article/details/83793709

https://blog.csdn.net/jisuanji12306/article/details/86363390
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值