一文了解线程间有哪些通信方式?
- 1. synchronized 内置锁
- 2. volatile 关键字
- 3. 等待/通知机制
- 3.1 等待
- wait()
- wait(long)
- wait(long, int)
- 等待方需遵循如下原则
- 3.2 通知
- notify()
- notifyAll()
- 通知方需遵循如下原则
- notify() 和 notifyAll() 应该用谁?
- 4. 管道输入/输出流
- 5. Thread.join()
- 面试题
- 6. ThreadLocal
1. synchronized 内置锁
关键字 synchronized 可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
2. volatile 关键字
关键字 volatile 可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。
3. 等待/通知机制
可以通过 Java 内置的等待/通知机制 - wait()、notify/notifyAll() 实现一个线程修改一个对象的值,而另一个线程感知到了变化,然后进行相应的操作。
3.1 等待
wait()
调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回。
需要注意
:调用 wait() 方法后,会释放对象的锁。
wait(long)
超时等待一段时间,这里的参数时间是毫秒,也就是等待长达 n 毫秒,如果没有通知就超时返回。
wait(long, int)
对于超时时间更细粒度的控制,可以达到纳秒。
等待方需遵循如下原则
-
获取对象的锁;
-
如果条件不满足,那么调用对象的 wait() 方法,被通知后仍要检查条件;
-
条件满足执行业务逻辑。
synchronized (对象) {
while (条件不满足) {
对象.wait();
}
do something;
}
3.2 通知
notify()
通知一个在对象上等待的线程,使其从 wait() 方法返回,而返回的前提是该线程获取到了对象的锁,没有获得锁的线程重新进入 WAITING 状态。
notifyAll()
通知所有等待在该对象上的线程
通知方需遵循如下原则
-
获取对象的锁;
-
改变条件;
-
通知所有等待在对象的线程。
synchronized (对象) {
改变条件;
对象.notifyAll();
}
notify() 和 notifyAll() 应该用谁?
尽可能用 notifyAll(),谨慎使用 notify()。因为 notify() 只会唤醒一个线程,我们无法确保被唤醒的这个线程一定就是我们需要唤醒的线程
4. 管道输入/输出流
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。
管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前两种面向字节,而后两种面向字符。
5. Thread.join()
如果一个线程A执行了 Thread.join() 语句,其含义是:当前线程A等待 Thread 线程终止之后才从 Thread.join() 返回。线程 Thread 除了提供 join() 方法之外,还提供了join(long millis) 和 join(long millis,int nanos) 两个具备超时特性的方法。
面试题
现在有 T1、T2、T3 三个线程,怎样保证 T2 在 T1 执行完后执行,T3 在 T2 执行完后执行?
用 Thread.join() 方法即可,在 T3 中调用 T2.join(),在 T2 中调用 T1.join()。
6. ThreadLocal
ThreadLocal,即线程变量,是一个以 ThreadLocal 对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个 ThreadLocal 对象查询到绑定在这个线程上的一个值。
可以通过 set(T) 方法来设置一个值,在当前线程下再通过 get() 方法获取到原先设置的值。