参考:
Java
并发编程的艺术
线程间通信的方式有:
- 通过
volatile
和synchronized
关键字 - 等待/通知机制
- 管道输入/输出流
Thread.join
等待线程终止ThreadLocal
线程本地变量
1 通过 volatile 和 synchronized 关键字
Java
支持多个线程同时访问一个对象或者对象的成员变量。
每个线程在自己的本地内存中都有共享变量的一份拷贝。
volatile
volatile
关键字用来修饰变量。任何对该变量的访问均需要从共享内存中获取,任何对该变量的修改必须同步刷新到共享内存中。保证了共享变量的 可见性、原子性。
synchronized
synchronized
可以修饰方法或者代码块。主要确保同一时刻,只能有一个线程能够访问同步代码,保证了线程对共享变量的 可见性、排他性。
2 等待/通知机制
等待/通知相关的方法是任意Java
对象都具备的。
方法名称 | 描述 |
---|---|
wait() | 调用该方法的线程进入WAITING 状态,只有等待另一个线程的通知或被中断才会返回。调用wait() 方法之后,会释放对象的锁。 |
wait(long) | 超时等待一段时间,参数是毫秒。只有等待另一个线程的通知或被中断,或者超时,就返回。 |
wait(long, int) | 超时等待一段时间,可以对超时时间更细粒度的控制,可以达到纳秒级。 |
notify() | 通知一个在对象上等待的线程,使其从wait() 方法返回。返回的前提是该线程获取到了对象的锁。 |
notifyAll() | 通知所有等待在该对象上的线程。 |
2.1 等待/通知的经典范式
等待方(消费者)遵循如下原则:
- 获取对象的锁。
- 如果条件不满足,那么调用对象的
wait()
方法,被通知(notify
)后仍要检查条件。 - 条件满足则执行对应逻辑。
synchronized (对象) {
while (条件不满足,需等待通知) {
对象.wait();
}
条件满足,对应处理逻辑
}
通知方(生产者)准寻如下原则:
- 获取对象的锁。
- 改变条件。
- 通知所有等待在对象上的线程。
synchronized (对象) {
改变条件
对象.notifyAll();
}
注意:无论是等待方还是通知方,首先都要先获得对象的锁。
3 管道输入/输出流
管道的输入/输出流,主要用于 线程之间的数据传输,传输媒介为 内存。
具体实现包括:
-
面向字节的
PipedOutputStream
、PipedInputStream
-
面向字符的
PipedReader
、PipedWriter
例子:
public class MainThread {
public static void main(String[] args) throws IOException {
PipedWriter writer = new PipedWriter();
PipedReader reader = new PipedReader();
// 将管道的输出流和输入流连接,否则在使用的时候会抛出IOException
writer.connect(reader);
// 启动子线程
Thread printThread = new Thread(new PrintThread(reader), "print-thread");
printThread.start();
// 主线程输出数据
char[] array = "Hello World.".toCharArray();
writer.write(array, 0, array.length);
}
}
/**
* 子线程
*/
class PrintThread implements Runnable {
/**
* 管道输入流
*/
private PipedReader reader;
public PrintThread(PipedReader reader) {
this.reader = reader;
}
@Override
public void run() {
int ch = 0;
try {
// 接收数据
while ((ch = reader.read()) != -1) {
System.out.print((char) ch);
}
} catch (IOException e) {
System.out.println();
e.printStackTrace();
}
}
}
注意:对于
Piped
类型的流,必须先调用connect()
方法将输入/输出流进行绑定。
4 Thread.join 等待线程终止
如果一个线程需要等待另一个线程执行一个任务之后再继续执行,可以使用Thread.join()
方法。
如果线程threadA
调用了threadB.join()
方法,那么线程threadA
进入WAITING
状态,需要等待线程threadB
终止之后才能从threadB.join()
处返回。
下表整理了线程提供的join
方法:
方法 | 说明 |
---|---|
join() | 等待线程终止后返回。 |
join(long) | 超时等待线程终止后返回,如果超时时间到了,立刻返回。 |
join(long, int) | 超时等待线程终止后返回,如果超时时间到了,立刻返回。纳秒级别的超时时间。 |
例子:
public class MainThread {
public static void main(String[] args) throws IOException, InterruptedException {
Thread joinThread = new Thread(new JoinThread(), "join-thread");
joinThread.start();
System.out.println("等待线程终止.");
joinThread.join();
System.out.println("从join()返回.");
}
}
/**
* 子线程
*/
class JoinThread implements Runnable {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " 执行任务...");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
5 ThreadLocal 线程本地变量
一个线程可以通过ThreadLocal
变量查询到绑定在这个线程上的值。
详细内容请查看: