概述
高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题
我们看到了 应用卡住了 。。。。 怀疑是不是死锁呢? (其实没有)
jstack或者可视化工具检测是否死锁(没有)
C:\Users\Mr.Yang>E: E:\>cd E:\Program Files\Java\jdk1.8.0_161\bin E:\Program Files\Java\jdk1.8.0_161\bin>jps 1504 MultiProduceConsumerDemo E:\Program Files\Java\jdk1.8.0_161\bin>jstack 1504 2019-10-01 00:44:23 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode): "DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002983800 nid=0x3348 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Thread-3" #14 prio=5 os_prio=0 tid=0x0000000019fa5000 nid=0x3af0 in Object.wait() [0x000000001abff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b682768> (a java.lang.Object) at java.lang.Object.wait(Object.java:502) at com.artisan.test.MultiProduceConsumerDemo.consume(MultiProduceConsumerDemo.java:42) - locked <0x000000078b682768> (a java.lang.Object) at com.artisan.test.MultiProduceConsumerDemo$2.run(MultiProduceConsumerDemo.java:63) "Thread-2" #13 prio=5 os_prio=0 tid=0x0000000019fa4000 nid=0x9e4 in Object.wait() [0x000000001aaff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b682768> (a java.lang.Object) at java.lang.Object.wait(Object.java:502) at com.artisan.test.MultiProduceConsumerDemo.consume(MultiProduceConsumerDemo.java:42) - locked <0x000000078b682768> (a java.lang.Object) at com.artisan.test.MultiProduceConsumerDemo$2.run(MultiProduceConsumerDemo.java:63) "Thread-1" #12 prio=5 os_prio=0 tid=0x0000000019fa0000 nid=0x37cc in Object.wait() [0x000000001a9ff000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b682768> (a java.lang.Object) at java.lang.Object.wait(Object.java:502) at com.artisan.test.MultiProduceConsumerDemo.produce(MultiProduceConsumerDemo.java:20) - locked <0x000000078b682768> (a java.lang.Object) at com.artisan.test.MultiProduceConsumerDemo$1.run(MultiProduceConsumerDemo.java:55) "Thread-0" #11 prio=5 os_prio=0 tid=0x0000000019f9f800 nid=0x1bd4 in Object.wait() [0x000000001a8fe000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b682768> (a java.lang.Object) at java.lang.Object.wait(Object.java:502) at com.artisan.test.MultiProduceConsumerDemo.produce(MultiProduceConsumerDemo.java:20) - locked <0x000000078b682768> (a java.lang.Object) at com.artisan.test.MultiProduceConsumerDemo$1.run(MultiProduceConsumerDemo.java:55) "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000019d2f800 nid=0x3ba4 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000019ca3800 nid=0x37e4 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000019c54800 nid=0x2db0 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000019c50800 nid=0x37bc waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000019c2e000 nid=0x2ed4 runnable [0x000000001a2fe000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x000000078b5dad50> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x000000078b5dad50> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000018860000 nid=0x34f8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000019c08800 nid=0x1514 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001883a000 nid=0x22c in Object.wait() [0x0000000019b9e000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b408ec0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x000000078b408ec0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002a73800 nid=0x29bc in Object.wait() [0x0000000019a9f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000078b406b68> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000078b406b68> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x0000000018818000 nid=0x381c runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002998000 nid=0x3024 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000299a000 nid=0x229c runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000299b800 nid=0x2bbc runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000299d000 nid=0x255c runnable "VM Periodic Task Thread" os_prio=2 tid=0x0000000019d67800 nid=0x1c5c waiting on condition JNI global references: 334 E:\Program Files\Java\jdk1.8.0_161\bin>
可以看到 并没有死锁的发生
或者 使用 jvisualvm 、 jmc 工具来看下都行
(jmc截图)
并且可以看到4个线程 均是 WAITING 状态
(jmc截图)
原因分析
为了方便观察,我们改造下,给线程起个名
package com.artisan.test; import java.util.stream.Stream; public class MultiProduceConsumerDemo { // 对象监视器-锁 private final Object LOCK = new Object(); // 是否生产出数据的标识 private boolean isProduced = false; // volatile 确保可见性, 假设 i 就是生产者生产的数据 private volatile int i = 0 ; public void produce(){ synchronized (LOCK){ System.out.println(Thread.currentThread() + " GOT LOCK " + isProduced); String msg = isProduced ? "已生产货物" : "没有货物可搬运"; if (isProduced){ try { System.out.println(Thread.currentThread() + " wait becauseof " + msg ); LOCK.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ i++; System.out.println(Thread.currentThread() + " Produce:" + i); LOCK.notify(); isProduced = true; } } } public void consume(){ // 加锁 synchronized (LOCK){ System.out.println(Thread.currentThread() + " GOT LOCK " + isProduced); String msg = isProduced ? "已生产货物" : "没有货物可搬运"; if (isProduced){ System.out.println(Thread.currentThread() + " Consume:" + i); LOCK.notify(); isProduced = false; }else{ try { System.out.println(Thread.currentThread() + " wait becauseof " + msg); LOCK.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { MultiProduceConsumerDemo produceConsumerDemo = new MultiProduceConsumerDemo(); Stream.of("P1","P2").forEach(n-> new Thread(n){ @Override public void run() { while(true) produceConsumerDemo.produce(); } }.start()); Stream.of("C1","C2").forEach(n->new Thread(n){ @Override public void run() { while(true) produceConsumerDemo.consume(); } }.start()); } }
运行下 ,可以看到程序并没有持续运行,而是假死了…
(每次运行的结果都有可能不一样,这里我们以这次的运行结果来分析下)
逐步分析下:
生产者的代码
消费者代码
- 线程P1锁,没有货物生产,isProduce=false
- 线程P1,生产货物 ,紧接着
LOCK.notify(); isProduced = true;
,其实第一步的LOCK.notify() 是没有什么作用的,因为没有任何线程wait. 执行完以后释放锁。 对应日志
- 紧接着,P1又抢到了锁,但是生产后没有被消费,所以直接进入
LOCK.wati.
执行完以后释放锁。P1-----WAITING . 对应日志 - P2同上一步P1的操作 ,P2-----WAITING 对应日志
C1 抢到资源 ,isProduce已经生产,所以C1线程直接消费,消费完成以后要通知生产者继续生产,即唤醒消费者 (LOCK.notify()
;),将isProduce标志位置为false . 执行完以后,释放锁
- C1-----WAITING日志如下
- 紧接着C1又抢到了锁,因没有生产,所以进入
LOCK.wait()
,释放锁。 对应日志
依次类推… 直到最后C2 唤醒了C1 ,此时C1看到isProduce=false, 则C1进入了wait ,这个时候4个线程都是watiing的状态了,就出现了4个线程均是wait状态,都不执行了,出现了假死 (因为notify方法,唤醒一个线程,具体是哪个线程是不确定的。)
那如何解决呢? 下篇博文我们一起来探讨下