线程间通讯

线程间通讯

1.使用wait/notify实现线程间通讯
2.生产者/消费者模式实现
3.方法join的使用
4.ThreadLocal类的使用
等待/通知机制

1.不使用等待/通知机制实现线程间通讯
  经过while(true)轮询机制检查某一个条件
  缺点:这样一个死循环们很是耗费cpu资源
2.什么是等待/通知机制
  产生数据时间不肯定,使用数据时间也不肯定。
  1.数据还未产生,数据使用者须要等待数据产生
  2.数据产生后通知数据使用者,这时数据使用者才知道数据有了,可使用数据
3.等待/通知机制的实现
  方法wait()做用,使当前执行代码的线程进行等待,将当前线程置入到"预执行队列"中,等待接到通知或被中断为止
                  使用wait方法必须得到对象级别的锁,因此只能在同步方法或同步代码块中使用
                  wait方法执行后,当前线程放弃锁,在从wait返回以前,须要和其余线程竞争获取锁
  方法notify()也须要得到对象级别的锁,因此也须要在同步方法或同步代码块中使用,若是有多个线程等待,线程规划器会随机
              挑选出一个呈wait等待的线程
  说明:执行notify()以后,当前线程并不会立马释放掉锁,而必须等待执行完synchronized代码块以后,同时wait()线程也不会立马得到锁
  notifyAll()方法可使全部正在等待队列中等待同一共享资源的所有线程从等待状态退出,进入可运行状态,到底哪一个线程要想执行,取决于
             线程执行的优先级,或者jvm的实现状况(随机的)。
  每一个锁对象有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列存储了将要得到锁的线程,阻塞队列存储了被阻塞的线程,一个线程
  被唤醒后以后就会进入到就绪队列,等待cpu调度,反之,当过一个线程wait以后就会进入阻塞队列。
4.方法wait()锁释放与notify()锁不释放
  当方法wait()被执行以后,锁自动释放,但执行完notify()方法,锁不会当即释放
5.当interrupt方法遇到wait方法
  当线程呈现wait状态时,调用线程对象的interrupt方法会出现异常InterruptedException

  以上总结:执行完同步代码块会释放锁
            执行同步代码块异常时,线程终止,会释放锁
            执行wait方法,线程会立马释放掉锁,此线程对象进入线程等待池,等待被唤醒
6.只通知一个线程
  调用notify()方法一次,只会随机通知一个线程进行唤醒
7.唤醒全部线程
  notifyAll()
8.方法wait(long)的使用
  带参数的wait(long)方法:等待某一时间是否有现成对锁进行唤醒,若是超过这个时间,就会自动唤醒1
9.通知过早
  若是通知过早,则会打乱程序运行的正常逻辑,就如同先通知了,再等待了,因此就无限等待了
10.等待wait条件发生变化
  若是wait等待条件发生变化,则会打乱程序运行的正常逻辑
11.生产者/消费者模式实现
  等待/通知模式最经典的案例就是生产者/消费者模式

  一辈子产与一消费:操做值
  一对一执行
  多生产与多消费:操做值 - 假死
  主要缘由:虽然代码中经过wait和notify通讯,可是并不能保证生产者发出的通知就必定会被消费者执行,也有多是生产者本身执行,长此下去,全部线程都会进入等待状态
  假死的缘由就是可能连续唤醒同类
  解决方式,通知改成notifyAll()
  一辈子产与一消费:操做栈
  一辈子产与多消费:操做栈:解决wait条件改变与加色、
  多生产与一消费:操做栈
  多生产与多消费:操做栈
12.经过管道进行线程之间的通讯(字节流)
  一个线程发送数据到输出管道,另外一个线程从输入管道中读数据
  PipedInputStream和PipedOutputStream
  PipedReader和PipedWriter
  使用inputStream.connect(outputStream)或outputStream.connect(inputStream)的做用使两个Stream之间产生通讯
13.实战:等待/通知之交叉备份
 使用volatile关键字共享变量,再结合等待通知实现
方法join的使用

join等待线程对象销毁。主线程建立并启动子线程,子线程当中有不少的耗时任务,主线程每每要早于子线程提早结束,这时,若是主线程要等子线程结束后再结束,就要用到join
1.方法join前的铺垫
  等子线程执行完再执行主线程
2.join方法解决
  方法join做用是使所属线程对象x正常执行run方法,而使当前线程z进行无限期的阻塞,当x线程执行完以后再继续执行z线程后面的代码
  底层用的是wait方法,操做的锁
  join具备使线程排队的做用,有点相似于同步效果
  join和synchronized区别:join内部使用wait方法进行等待,而synchronized使用的是对象监视器。
3.方法join与异常
  join方法遇到interrupt方法遇到会抛出异常
  例如线程x运行,y线程调用x线程,并join x线程,当y线程执行interrupt方法,y线程会抛出异常,可是x线程仍是会继续运行。
4.方法join(long)的使用
  方法join(long)中的参数是设定等待的时间
5.join(long)与sleep(loong)区别
  join(long)内部使用的是wait(long)方法来实现的,因此具备释放锁的特色
  Thread.sleep(long)不释放锁,会持有锁
6.方法join()后面的代码提早运行:出现意外
  join必须获得锁,才会继续运行
类ThreadLocal的使用

变量值的共享可使用public static变量的形式,全部线程都是用同一个public static变量,这个只能存一个值,并非对象,list,map、等
实现每一个线程有本身的共享变量 要用到ThreadLocal
1.方法get与null
  类ThreadLocal解決的是变量在不一样线程间的隔离性,也就是不一样线程拥有本身的值,不一样线程的值能够放在ThreadLocal类中进行保护
2.验证线程变量的隔离性
  第一次get()返回的是null值
3.解决get()返回null值问题
  建立一个类,继承自ThreadLocal,覆盖他的初始化方法initialValue(),给他初始化的值
4.再次验证线程变量的隔离性
类InheritableThreadLocal的使用

在子线程中取得父线程继承下来的值,若是子线程在取值的同时,主线程改变了值,这时候,子线程获得的仍是旧值