等待/通知模式
wait()
方法将当前线程置入"预执行队列"中,释放锁,直到收到通知或者到达规定时间,才会继续参与对锁的争夺,得到锁后,从wait()
代码位置开始继续执行,只有在同步方法或同步块中使用wait()
,否则抛出IllegalMonitorStateException
(非受查异常,不需要catch)notify()/notyfyAll()
也要在同步方法或同步块中调用,否则抛出IllegalMonitorStateException
;用来通知wait的线程来争夺锁,但不释放锁,直到同步代码块执行完毕wait()
和notify()/notifyAll()
是针对同一把锁来说的- 通知过早,导致
wait()
的线程再也不被唤醒
生产者/消费者模式
- 假死:全部线程进入waitting状态
- 生产者持续生产,直到缓冲区满,阻塞;缓冲区不满后,继续生产
- 消费者持续消费,直到缓冲区空,阻塞;缓冲区不空后,继续消费
- 生产者可以有多个,消费者也可以有多个
三种实现
- BlockingQueue
- wait && notify
- Lock && Condition
判断模型的正确性
- 同一产品的消费行为一定发生在生产行为之后
- 任意时刻,缓冲区大小不小于0,不大于限制容量
通过管道进行线程间通信:字节、字符流
PipeInputStream和PipeOutputStream
PipedReader和PipeWriter
outputStream.connect()来连接两个管道
阻塞,没有数据可读的时候会阻塞
join的使用
x.join()
,x正常执行,而当前线程则被阻塞,直到x被销毁后,继续从join处执行当前线程的代码join(long)
设定等待的时间,内部用wait()
来实现,所以会释放锁join()
遇上interrupt()
会出现异常
public class ThreadB extends Thread{
@Override
synchronized public void run() {//synchronized的有无影响结果
try {
System.out.println("begin B ThreadName = " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("begin B ThreadName = " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
}
catch (Exception e){
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
// write your code here
ThreadB b = new ThreadB();
// ThreadA a = new ThreadA(b);
// a.start();
b.start();
b.join(1000);
System.out.println("main end "+ System.currentTimeMillis());
}
如果无synchronized关键字
begin B ThreadName = Thread-0 1556087741789
main end 1556087742789
begin B ThreadName = Thread-0 1556087743816
如果有synchronized关键字
begin B ThreadName = Thread-0 1556087786059
begin B ThreadName = Thread-0 1556087788090
main end 1556087788090
可以发现join(1000)
方法并没有在1秒后恢复主线程的执行,这是因为synchronized
关键字导致线程b一直占用了这个锁,join
只有在线程b结束后才能得到这个锁 。
如果加上ThreadA,取消掉main中关于a的注释
public class ThreadA extends Thread {
private ThreadB b;
public ThreadA(ThreadB b) {
this.b = b;
}
@Override
public void run() {
try{
synchronized (b){
System.out.println("begin A ThreadName = "+ Thread.currentThread().getName()+" "+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("end A ThreadName = "+ Thread.currentThread().getName()+" "+System.currentTimeMillis());
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
结果可能有很多种,在我的机器上是这样的
begin A ThreadName = Thread-1 1556088325106
end A ThreadName = Thread-1 1556088327136
begin B ThreadName = Thread-0 1556088327137
main end 1556088327136
begin B ThreadName = Thread-0 1556088329139
运行结果的解释:
b.join()
方法先抢到B锁,然后将B锁释放- ThreadA抢到锁,打印内容,sleep,打印内容
b.join()
和ThreadB来争抢锁,join抢到,发现时间已过,释放锁- ThreadB得到锁,打印
- 主线程继续往下执行,打印
- ThreadB同时sleep,打印
b.join(1000)
方法的执行需要得到,ThreadB这把锁,只有得到锁后,才会进入join方法;
如果我们把上述代码的A,B的锁改为其他的,就会得到如下结果
begin A ThreadName = Thread-1 1556088940831
main end 1556088941832//没人和join争抢锁,超出等待时间后便结束了
end A ThreadName = Thread-1 1556088942859
begin B ThreadName = Thread-0 1556088942859
begin B ThreadName = Thread-0 1556088944861
ThreadLocal
- 类ThreadLocal主要解决的是每个线程绑定自己的值,从而不同线程可以拥有与自己的值;
get()
(默认返回null)和set()
方法来绑定,获取值;- 通过继承ThreaLocal类,覆写
initialValue()
方法来设置初始值