Java多线程编程核心技术第三章小笔记:线程间通信

第一节:等待/通知机制

  1. 等待/通知机制出现的意义:减少CPU的资源浪费,而且还可以实现在多个线程间通信;
  2. 线程等待方法:Object类的wait()方法,将当前线程置入“预执行队列”,必须在synchronized方法或者代码块中,如果不加入同步块就会出现这个异常:java.lang.IllegalMonitorStateException;;执行后,当前线程释放锁,如果调用wait方法时没有持有适当的锁,抛出异常IllegalMonitorStateException,(RuntimeException)的一个子类,所以不需要try…catch语句进行捕获异常;
  3. 线程唤醒:Object类的notify方法,如果没有持有当前线程的锁也会抛出异常IllegalMonitorStateException,
    执行该方法后,当前线程不会立即释放该对象锁,呈wait状态的线程也并不会马上获取该对象锁,要等待执行notify方法的线程将程序执行完,也就是退出同步块之后线程才会释放锁。
  4. 每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列;就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程;
  5. 小总结: * notify方法执行完同步代码块就会释放对象锁 *
    执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放; *
    执行了wait方法后,该线程会释放对象锁,并且进入线程等待池中,等待被唤醒;
  6. 当线程呈wait状态时,调用线程对象的interrupt方法会出现InterruptedException异常
  7. notify()方法仅可以随机唤醒一个线程;而notifyAll()方法可以唤醒全部等待中的线程;
  8. 方法wait(long):等待long时间内是否有线程对锁唤醒,如果超过这个时间则自动唤醒;当然在这个时间内也可以由其他线程所唤醒;
  9. 两个线程,若是A线程先调用了notify方法,然后B线程再调用wait方法,那么B方法是不能被唤醒的,B方法会一直呈wait状态;
  10. 生产者消费者模型:练习;
  11. 通过管道进行线程间通信;管道流(pipeStream),用于在不同线程间之间传送数据。一个线程从输入管道中读取数据,通过使用管道,实现不同线程间的通信,从而无须借助类似临时文件之类的东西;

第二节:方法join的使用;

  1. join的由来:等待调用join这个方法的线程执行完毕后再进行下面的代码;

  2. join的作用:使所属线程对象x正常执行run方法里的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后再继续执行线程z后面的代码,;使线程排队运行,有些类似同步的运行效果;

  3. join与synchronized的区别:join在内部使用wait方法进行等待,而synchronized关键字使用的是“对象监视器”原理作为同步;

  4. 3、方法join遇到中断时的情况,启动的同时调用interrupt方法来中断线程,
    会抛出异常java.lang.InterruptedException

    举个例子:

/**
 1. 观察方法join与异常的发生
 2. 如果当前线程对象被中断,则当前线程出现异常
 3. 调用interrupt方法之后
*/
class MyThread3A extends Thread{
    @Override
    public void run() {
        for (int i = 0;i < 10;i++){
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前数字"+(i+1));
        }
    }
}
//定义两个线程
class MyThread3B extends Thread{
    @Override
    public void run() {
        try{
            MyThread3A a = new MyThread3A();
            a.start();
            a.join();
            System.out.println("线程B在最后打印了---");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
class MyThread3C extends Thread{
    private MyThread3B myThread3B;
    public MyThread3C(MyThread3B myThread3B) {
        this.myThread3B = myThread3B;
    }
    @Override
    public void run() {
        myThread3B.interrupt();
    }
}
public class Test3 {
    public static void main(String[] args) {
        try{
            MyThread3B myThread3B = new MyThread3B();
            myThread3B.start();
            Thread.sleep(500);
            MyThread3C myThread3C = new MyThread3C(myThread3B);
            myThread3C.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

运行结果:
出现异常的原因是线程B已经停止,但是线程A并未出现异常,A在线程B里面调用了join方法,依旧是正常执行的状态直到执行完毕;
Java多线程编程核心技术第三章小笔记:线程间通信_第1张图片
4. 区别一下join(long)跟sleep(long)的区别
区别一下join(long)跟sleep(long)的区别:

第三节:类ThreadLocal的使用:

  1. ThreadLocal实现了每一个线程都有自己的共享变量,绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有变量;ThreadLocal类解决了变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程中的值可以放入ThreadLocal类中进行保存;
  2. ThreadLocal类的对象初始化默认值为null;
  3. 解决第一次get返回null的问题: 在ThreadLocal类中有一个子类专用的方法:这个方法就是初始化值得,子类覆写这个方法,return一个初始值;
protected T initialValue() {
    return null;
}
  1. 子线程和父线程同样拥有各自得私有值;
  2. 类InheritableThreadLocal:使用该类可以让子线程从父线程中取得值;
  3. 值继承再修改:类InheritableThreadLocal提供的修改父线程值得方法;
protected T childValue(T parentValue) {
    return parentValue;
}
  • 此处注意一下,在使用InheritableThreadLocal类需要注意一点:如果子线程在取得值得同时,主线程将InheritableThreadLocal中得值进行修改,那么子线程取得的值还是旧值;

你可能感兴趣的:(Java多线程编程核心技术)