Chapter 3
使用线程间通信,会增强系统间的交互性,提高 CPU 的利用率。
这章的核心内容如下:
- 使用 wait()/notify() 实现线程之间通信
- 生产者/消费者模式的实现
- join() 方法的使用
- ThreadLocal 类的使用
1
不使用 wait()/notify() 如何实现线程之间通信?
创建2个线程,2个线程中放入同一对象,A中遍历添加元素到集合中,B中用while来执行死循环获取当集合长度为多少时抛出异常。这是简单的一个案例。
等待/通知机制实现
通过 wait()/notify() 。他们都是 Object 类的方法。
wait()
- 使线程停止运行
- 只能在同步方法或者同步代码快中执行
- wait() 执行后,释放当前对象锁。执行前没有合适锁对象,会抛出异常。
- notify()
- 使停止的线程继续运行
- 只能在同步方法或者同步代码快中执行
- 执行前没有合适锁对象,会抛出异常,执行后,只有退出了synchronized方法后才会释放当前对象锁。
- 当线程数很多的时候,用notifyAll()唤醒所有线程
- wait(long) 在某个时间内是否有线程对其唤醒,否则超时自动唤醒.
如果通知过早,会影响逻辑性(但是线程启动不是有随机性嘛,小概率事件会正常,不过我运行很多次都是影响的,囧)
- 等待/通知机制的经典案例–生产者/消费者模式
多生产者-多消费者:假死
原因:有可能notify()唤醒的是同类,积少成多,造成假死
解决:将异类一起唤醒即可,使用notifyAll()
- 解决wait()条件改变与假死
原因:多个线程在wait后重新执行时,后面的操作异常导致。
解决:使用while通过管道实现进程间通信
- pipeSteam:同于在不同线程间直接传送数据。
- 字节流-PipedInputStream/PipedOutputStream
- 字符流-PipedReader/PipedWriter
- pipeSteam:同于在不同线程间直接传送数据。
2
join() 等待线程对象的销毁
场景:当需求是要在子线程操作一个数据,主线程需要获取这个值。
特点:底层是 wait() 实现,会释放锁。情况一
join(long) 后面的代码提前运行,why?
设置等待时间超时,释放锁,执行后面的代码
3
ThreadLocal – 让每个线程绑定自己的值
- 原因:变量值的共享可以用public static ,所有线程都共享同一个static变量,每个线程如果要实现自己的共享变量呢?
- 解决:ThreadLocal 让每个线程都有自己的共享变量。就像一个全局存放数据的盒子,盒子中存储每个线程的私有数据。
- initialValue 设置初始值
- set()/get() 设置/获取值
InheritableThreadLocal – 使子线程从父线程中取值
- 主线程和子线程的区别
UI界面和Main函数均为主线程。
被Thread包含的“方法体”或者“委托”均为子线程。- 子线程取到值的同时,主线程更改值,那么会取到旧值