前言
多线程的使用必然涉及到线程间的通信
一、等待/通知机制
不使用等待/通知机制实现线程间通信
举例:while死循环配合sleep()方法使用时
原因:不断使用while语句轮询检测某个条件,会浪费cpu。轮询时间间隔小,更浪费cpu资源
解决:实现线程间的通信,使用等待/通知机制
等待通知机制简介
举例:厨师做菜,放到窗口,服务员上菜的过程。厨师在做菜的过程,服务员就是在等待;厨师做完菜,放到窗口,就是通知。
问题:前两章多个线程同时访问同一个变量是不是等待/通知机制?
不是,多线程同时访问同一变量是线程主动读取的,而且有可能读取的数据是已经被改变的,并不是想要的数据了。
等待/通知机制的实现
实现:使用wait()方法and notify()/notifyAll方法
wait:使当前执行代码的线程等待,直到接到通知或被中断。执行前要有对象锁,如果没有就报异常;执行wait后,当前线程释放锁,从wait返回前,与其他线程竞争,重新获得锁。
notify:在同步方法或同步块中调用,调用前,必须要有锁,没有锁就报异常。执行notify后,也不会马上释放对象锁,执行方法的线程将程序执行完才会释放。
notifyAll:使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入运行态。运行顺序取决于JVM的实现。
状态转换图:
线程进入Runnable的情况:
线程进入阻塞状态的情况:
wait和sleep对比?
wait针对的是对象,执行过程中有解锁上锁的过程
sleep 对应的是线程,不会有解锁上锁的过程。
wait方法
wait状态下,执行interrupt会报异常,为什么?
方法wait(long)的使用:
1.参数是个时间,过了这个时间,方法自动解除wait状态,开始运行。
2.或者在这个时间内,被其他线程唤醒。
notify与notifyAll方法
方法notify只随机唤醒一个线程,多次调用,多次随机。
使用notifyAll方法,可以唤醒所有线程。
程序逻辑混乱的情况
1.过早通知,打乱程序逻辑
2.wait等待的条件发生变化
生产者消费者模式
一个生产者一个消费者或多个生产者消费者
可怕的假死状态
多生产者对多消费者,容易到假死状态,比如生产者唤醒生产者,消费者唤醒消费者,这种情况多了,就会假死。
举例:在一个whil循环里,生产者1通知消费者1,但是过早通知了,所以还没有运行完全,就释放锁。继续循环,发现没有被消费,进入等待状态。生产者2被启动了,依次类推。。。
解决假死
1.操作值:一生产对一消费。将notify改成notifyAll
2.操作栈:一生产对一消费,一生产多消费,多生产一消费,多生产多消费。
从List栈中取数据,使List最大容量为1
线程间通信
利用管道流。一个线程发送数据到输入管道,另一个线程从输入管道读取。
1.字节流:PipedReader、PipedOutputStream
2.字符流:PipedReader、PipedWriter
二、join方法的使用
join方法:等待线程对象销毁
原理:等待子线程运行完再结束。在内部使用wait方法进行等待。
问题:join和interrupt遇到会有异常,因为当前线程的对象被中断
join(long)的使用
1.参数依然是设定的等待时间
join(long)和sleep(long)的区别?
锁的释放
三、类ThreadLocal的使用
是全局存放数据的盒子,盒子中可以存放每个线程的私有数据
作用:使线程变量具有隔离性
四、类InheritableThreadLocal
可以让子线程获取父线程的值
假如子线程取的值时,父线程改了这个值,子线程获取的是未被更改的。