等待\通知机制
- 如果不使用等待\通知机制,当线程1要向线程2实现线程通讯,可以通过共享一个变量,线程2可以使用while不停地对某一条件进行检测,直到线程1对其进行了修改(从而使条件满足),就实现了一次线程1到线程2的通讯,不过这样将浪费CPU的资源。
实现
-
wait()方法:
- Object类的一个方法,该方法将当前线程置入预执行队列中,并且在wait()代码处停止执行,直到接受到通知来或被中断为止。
- 在调用wait()方法之前,线程必须获得该对象的对象级别锁。否则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,不需要捕捉异常。
- 执行wait()方法之后,当前线程释放锁。
- wait(long):如果wait方法在long时间内没有被通知,那么超过这个时间则自动唤醒。
-
notify()/notifyAll()方法:
- Object类的一个方法,该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程,则由线程规划随机挑选出一个呈wait状态的线程对它发出通知notify,并使它等待获取该对象的对象锁。(notifyAll将通知持有该对象的所有其他线程)
- 调用notify()方法前,线程必须获得该对象的对象级别锁。否则抛出IllegalMonitorStateException。
- 执行notify()之后,当前线程并不会马上释放该对象锁,呈wait状态的线程也不能马上获取该锁。在等到执行notify()的方法将程序执行完,也就是退出synchronized代码块后,才会开始上述过程。
管道流
示例:
要点
在Java语言中提供了各种各样的输入、输出流,其中管道流(PipeStream)是一种特殊的流,用于在不同线程间直接传送数据。
Java IO中的管道为运行在同一个JVM中的两个线程提供了通信的能力。所以管道也可以作为数据源以及目标媒介。
你不能利用管道与不同的JVM中的线程通信(不同的进程)。在概念上,Java的管道不同于Unix/Linux系统中的管道。在Unix/Linux中,运行在不同地址空间的两个进程可以通过管道通信。在Java中,通信的双方应该是运行在同一进程中的不同线程。
可以通过Java IO中的PipedOutputStream
和PipedInputStream
创建管道。一个PipedInputStream流应该和一个PipedOutputStream流相关联。一个线程通过PipedOutputStream写入的数据可以被另一个线程通过相关联的PipedInputStream读取出来。PipedReader
和PipedWriter
同理。
join方法
示例:
class TestThread implements Runnable{
public void run(){
System.out.println("TestThread");
}
}
public class Test{
public static void main(String[] args){
try{
TestThread testThread = new TestThread();
testThread.start();
testThread.join();
System.out.println("Main");
} catch (InterruptedException e{
e.printStackTrace();
}
}
结果:
TestThread
Main
要点
- 方法join的作用是使被调用的线程对象正常执行run()方法中的任务,而调用线程将进行无限期的阻塞直到被调用线程被销毁后,才执行后面的代码。
- 换一种思考方式,方法join具有使线程排队运行的作用,有些类似于同步的运行效果。
- join()方法执行过程中,如果被执行线程遇到interrupt()方法,那么将会抛出InterruptedException异常。
- join(long):如果被执行方法在long时间内依旧没有被销毁,那么超过这个时间从join方法中返回执行后面的代码。方法join(long)的功能在内部是通过wait(long)实现的,所以也具有释放锁的特点。
类InheritableThreadLocal的使用
示例:
public class ThreadLocalParentSon {
//public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
public static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();
static class MyThread extends Thread{
@Override
public void run(){
System.out.println("Son Thread = " + threadLocal.get());
}
}
public static void main(String[] args) {
threadLocal.set(new Integer(127));// 当前线程设置值
Thread thread = new MyThread();
thread.start();
System.out.println("Main Tread = " + threadLocal.get());
}
}
Main Tread = 127
Son Thread = 127
若没有使用 InheritableThreadLocal 之前的输出结果:
Main Tread = 127
Son Thread = null
要点
- 使用InheritableTreadLocal类可以让子线程从父线程中取得值。
- 需要注意的一点是,如果子线程在取得值得同时,主线程将InheritableThreadLocal中的值进行修改,那么子线程取到的值还是旧值。