线程协作的三种方式
线程之间需要进行通信,通信有数据共享和线程协作两种方式,这篇主要说线程协作的内容。
一:数据共享
1:文件共享;2:网络共享;3:变量共享。
二:线程协作
先来个场景:落魄程序员摆摊卖起了炒粉,起先有人去买炒粉,发现炒粉卖完了,只能失落的回家了;后来为了不让客户白来一趟,落魄程序员想到了一个办法,线上预定。要是没有炒粉了,客户就不要白跑了,要是炒粉做好了,就通知客户。
2.1 被弃用的suspend和resume
suspend会让当前线程挂起,resume会唤醒当前线程。那么,举个栗子先:
上述代码运行结果如下:
第二个栗子:
运行结果:
发现线程一直挂起了,区别就是加了synchronized锁,客户买不到就把自己关了起来,想要通知到客户,就要拿到钥匙,可是客户把钥匙也拿起来了,所以就通知不到,造成了死锁。
注意:那是因为在synchronized中没有释放锁这个语义的,所以锁不能够进行释放,别人也就获取不到,然后就是死锁了
第三个栗子:
上述例子呢,落魄程序员2点就把炒粉做好了,去通知客户,可是客户5点了还在睡觉,运行结果如下:
同样的,会造成死锁。
2.2 wait和notify,notifyAll
wait会让当前线程挂起,而且当线程调用wait之后,会自动释放锁,notify,notifyAll会唤醒线程,wait和notify,notifyAll只能用在synchronized关键字中,而且必须是同一个对象锁,否则会报java.lang.IllegalMonitorStateException异常。
又来栗子了:
public static Object obj; public static void main(String[] args) throws InterruptedException { Demo_Wait wait = new Demo_Wait(); wait.test1(); } //正常,不会死锁 public void test1() throws InterruptedException { Thread thread = new Thread(new Runnable() { @Override public void run() { while (obj == null) { synchronized (Demo_Wait.class) { try { System.out.println("客户来预定炒粉,炒粉卖完了,不开心。。。"); Thread.currentThread().wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("客户买到了炒粉,开心回家吸粉了!!!"); } }); thread.start(); Thread.sleep(2000); obj = new Object(); synchronized (Demo_Wait.class) { Thread.currentThread().notifyAll(); System.out.println("落魄程序员做好了炒粉,通知客户。"); } }
下一个栗子:
同样的,落魄程序员2点就把炒粉做好了,去通知客户,可是客户6点了还在睡觉,造成了死锁。运行结果如下:
2.3 park和unpark
park会让线程挂起,unpark唤醒线程。话不多说,栗子来啦:
运行结果如下:
第二个栗子:
运行结果:
发现,先唤醒后挂起并不会死锁,原因是park,unpark是许可的意思,也就是说你只要有了许可证,就可以通过,若unpark多次,只当作一次,后续过来的park会继续等待。
第三个栗子:
运行结果如下:
同样的,park和unpark加了synchronized关键字会造成死锁。
2.4 总结
协作方式 | 加synchronized关键字 | 先唤醒后挂起 |
suspend/resume | 死锁 | 死锁 |
wait/notify,notifyAll | 不会死锁 | 死锁 |
park/unpark | 死锁 | 不会死锁 |