读书笔记——《Java多线程编程核心技术》第三章

线程状态切换示意图:

线程状态切换

变量在内存中的工作过程如下:

变量在内存中的工作过程

wait,notify与notifyAll()

wait()方法可以使调用该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到被再次唤醒。
notify()方法可以随机唤醒等待队列中等待同一共享资源的“一个线程”,并使该线程退出等待队列,进入可运行状态,也就是notify()方法通知“一个”线程。
notifyAll()方法可以使所有正在等待队列中等待同一共享资源的“全部”线程从等待状态退出,进入可运行状态。

需要注意的是,wait()执行后会立即释放锁,notify()执行后并不会立即释放锁,而是等到执行notify()方法的线程退出synchronized代码块后才释放锁。

wait(long)的使用

带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒。

生产者-消费者模型(注意线程假死)

通过管道进行线程间通信:字节流/字符流

  1. PipedInputStream和PipedOutputStream
  2. PipedReader和PipedWriter

在使用管道的时候,要进行如下操作:
inputStream.connect(outputStream);

outputStream.connect(inputStream);

join()的使用

join的作用是等待线程对象销毁。主线程创建并启动子线程,如果子线程中要进行大量的耗时运算,主线程往往早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,就用到了join。

public class MyThread extends Thread {

    @Override
    public void run(){
        try{
            int secondValue = (int)(Math.random() * 10000);
            System.out.println(secondValue);
            Thread.sleep(secondValue);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
}
主线程代码如下:
public class Run17 {

    public static void main(String[] args) {

        try{
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join();
            System.out.println("我想当threadTest对象执行完毕后我再执行");
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

}

//Output
8196
我想当threadTest对象执行完毕后我再执行

join(long)的使用

方法join(long)中的参数是设定等待的时间。在程序中,我们将join(long)改为sleep(long),从运行结果来看是相同的,那么这两个方法有什么不同呢?

方法join(long)的功能在内部是使用wait(long)方法来实现的,所以join(long)方法具有释放锁的特点。源码如下:

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

从源码可以看到,当执行wait(long)方法时,当前线程的锁被释放,那么其他线程就可以调用此线程中的同步方法了。而Thread.sleep(long)方法却不释放锁。

ThreadLocal的使用

作用是每个线程绑定自己的值,可以理解为HashMap。要想ThreadLocal有初始值,那么实现initialValue()方法即可。

类InheritableThreadLocal的使用

使用类InheritableThreadLocal的使用可以让子线程从父线程中取得值。实例代码如下:

public class InheritableThreadLocalExt extends InheritableThreadLocal<String> {

    @Override
    protected String initialValue() {
        return new Date().getTime()+"";
    }

    @Override
    protected String childValue(String parentValue) {
        return parentValue + " 我在子线程加的~!";
    }

}

public class Tools1 {

    public static InheritableThreadLocalExt tl = new InheritableThreadLocalExt();
}

子线程代码如下:
public class ThreadA11 extends Thread {

    @Override
    public void run(){
        try{
            for(int i = 0; i < 2; i++){
                System.out.println("在ThreadA线程中取值=" + Tools1.tl.get());
                Thread.sleep(100);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

}

主线程代码如下:

public class Run26 {

    public static void main(String[] args) {

        try{
            for(int i = 0; i < 2; i++){
                System.out.println("在Main线程中取值=" + Tools1.tl.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA11 a = new ThreadA11();
            a.start();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

}

 //Output
在Main线程中取值=1486978985517
在Main线程中取值=1486978985517
在ThreadA线程中取值=1486978985517 我在子线程加的~!
在ThreadA线程中取值=1486978985517 我在子线程加的~!