java线程通讯的方式主要有三种
suspend/resume
- wait/notify
- park/unpark
suspend/resume
- 正常的suspend/resume
public void suspendResumeTest() throws Exception { // public static Object baozidian = null; // 启动线程 Thread consumerThread = new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 System.out.println("1、进入等待"); Thread.currentThread().suspend(); } System.out.println("2、买到包子,回家"); }); consumerThread.start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); consumerThread.resume(); System.out.println("3、通知消费者"); }
- 死锁的suspend/resume: suspend并不会像wait一样释放锁,故此容易写出死锁代码
public void suspendResumeDeadLockTest() throws Exception { // public static Object baozidian = null; // 启动线程 Thread consumerThread = new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 System.out.println("1、进入等待"); // 当前线程拿到锁,然后挂起 synchronized (this) { Thread.currentThread().suspend(); } } System.out.println("2、买到包子,回家"); }); consumerThread.start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); // 争取到锁以后,再恢复consumerThread synchronized (this) { consumerThread.resume(); } System.out.println("3、通知消费者"); }
- 导致程序永久挂起的suspend/resume
public void suspendResumeDeadLockTest2() throws Exception { // public static Object baozidian = null; // 启动线程 Thread consumerThread = new Thread(() -> { if (baozidian == null) { System.out.println("1、没包子,进入等待"); try { // 为这个线程加上一点延时 Thread.sleep(5000L); } catch (InterruptedException e) { e.printStackTrace(); } // 这里的挂起执行在resume后面 Thread.currentThread().suspend(); } System.out.println("2、买到包子,回家"); }); consumerThread.start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); consumerThread.resume(); System.out.println("3、通知消费者"); consumerThread.join(); }
wait/notify
- 正常的wait/notify
public void waitNotifyTest() throws Exception { // public static Object baozidian = null; // 启动线程 new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 synchronized (this) { try { System.out.println("1、进入等待"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("2、买到包子,回家"); }).start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); synchronized (this) { this.notifyAll(); System.out.println("3、通知消费者"); } }
- 会导致程序永久等待的wait/notify
public void waitNotifyDeadLockTest() throws Exception { // public static Object baozidian = null; // 启动线程 new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 try { Thread.sleep(5000L); } catch (InterruptedException e1) { e1.printStackTrace(); } synchronized (this) { try { System.out.println("1、进入等待"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("2、买到包子,回家"); }).start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); synchronized (this) { this.notifyAll(); System.out.println("3、通知消费者"); } }
park/unpark
- 正常的park/unpark
public void parkUnParkTest() throws Exception { // public static Object baozidian = null; // 启动线程 Thread consumerThread = new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 System.out.println("1、进入等待"); LockSupport.park(); } System.out.println("2、买到包子,回家"); }); consumerThread.start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); LockSupport.unpark(consumerThread); System.out.println("3、通知消费者"); }
- 死锁的park/unpark
public void parkUnParkDeadLockTest() throws Exception { // public static Object baozidian = null; // 启动线程 Thread consumerThread = new Thread(() -> { if (baozidian == null) { // 如果没包子,则进入等待 System.out.println("1、进入等待"); // 当前线程拿到锁,然后挂起 synchronized (this) { LockSupport.park(); } } System.out.println("2、买到包子,回家"); }); consumerThread.start(); // 3秒之后,生产一个包子 Thread.sleep(3000L); baozidian = new Object(); // 争取到锁以后,再恢复consumerThread synchronized (this) { LockSupport.unpark(consumerThread); } System.out.println("3、通知消费者"); }
总结
1.suspend/resume对调用顺序有要求,也要开发自己注意锁的释放。这个被弃用的API, 容易死锁,也容易导致永久挂起。
2.wait/notify要求再同步关键字里面使用,免去了死锁的困扰,但是一定要先调用wait,再调用notify,否则永久等待了
3.park/unpark没有顺序要求,但是park并不会释放锁,所以在同步代码中使用要特别注意,防止出现死锁