一文详解wait与notify

内容简介

本章需要重点掌握的技术点如下:
1)使用wait/notify实现线程间的通信。
2)生产者/消费者模式的实现。
3)方法join的使用。
4)ThreadLocal类的使用。

3.1 等待/通知机制

3.1.1 不使用等待/通知机制实现线程间的通信

public class MyList {
    private List list = new ArrayList();
    public void add(){
        list.add("");
    }
    public int size(){
        return list.size();
    }
}
public class ThreadA extends Thread {
    private MyList myList;
    public ThreadA(MyList myList) {
        this.myList = myList;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                myList.add();
                System.out.println("添加了" + (i + 1) + "个元素");
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ThreadB extends Thread {

    private volatile MyList myList;

    public ThreadB(MyList myList) {
        this.myList = myList;
    }

    @Override
    public void run() {
        try {
            while (true) {
                // System.out.print("");
                if (myList.size() == 5) {
                    System.out.println("==5,线程b退出来");
                    throw new InterruptedException();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        MyList myList = new MyList();
        ThreadA a = new ThreadA(myList);
        a.setName("a");
        a.start();
        ThreadB b = new ThreadB(myList);
        b.setName("b");
        b.start();
    }
}
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
添加了5个元素
==5,线程b退出来
java.lang.InterruptedException
	at part6.ThreadB.run(ThreadB.java:18)
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素

虽然两个线程实现了通信,但是是通过while语句轮询检测实现的,轮询时间过小浪费CPU资源,轮询时间过大有可能会取不到想要的数据,这时候就可以用到wait/notify机制。

3.1.3 等待/通知机制的实现

wait()的作用是使当前执行代码的线程进行等待。wait()是Object类的方法, 该方法用来将当前线程置入“预执行队列”中,在调用wait()所在代码行处停止执行,直到接到通知或被中断 。在调用wait()之前,线程必须获取该对象的对象级别锁,只能在同步方法或同步代码块中调用wait()方法。执行wait()之后,当前线程释放锁。如果调用wait()时没有持有适当的锁,则会抛出IllegalMonitorStateException

方法notify(),也要在同步方法或同步代码块中调用,用来通知那些等待该对象锁的其他线程,如果多个线程等待,则由线程规划器随机挑选一个呈wait状态的线程,对其发起通知。在执行notify()方法后,当前线程不会马上释放对象锁,当前线程释放该对象锁之后,呈wait状态的线程才可以获取锁。如果调用notify()时没有持有适当的锁,则会抛出IllegalMonitorStateException

public class Test {
    public static void main(String[] args) throws InterruptedException {
        String lock = new String();
        System.out.println("sync上面");
        synchronized (lock){
            System.out.println("sync第一行");
            lock.wait();
            System.out.println("wait下面");
        }
        System.out.println("end");
    }
}
sync上面
sync第一行

根据上面的执行结果来看,线程进入wait状态,不会继续向下运行,让它继续执行,可以使用notify()notifyAll()

public class Mythread1 extends Thread {
    private Object lock;
    public Mythread1(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("start wait time =" + System.currentTimeMillis());
                lock.wait();
                System.out.println("end wait time   =" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Mythread2 extends Thread {

    private Object lock;

    public Mythread2(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            System.out.println("start notify time =" + System.currentTimeMillis());
            lock.notify();
            System.out.println("end   notify time =" + System.currentTimeMillis());
        }
    }
}
public class Run {

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        Mythread1 mythread1 = new Mythread1(lock);
        mythread1.start();
        Thread.sleep(3000);
        Mythread2 mythread2 = new Mythread2(lock);
        mythread2.start();
    }
}
start wait time =1588821820237
start notify time =1588821823237
end   notify time =1588821823237
end wait time   =1588821823237

从上面打印信息来看,Mythread1先执行进入wait状态,3秒后被notify唤醒。

public class MyList {
    private static List list = new ArrayList();
    public static void add() {
        list.add("");
    }
    public static int size() {
        return list.size();
    }
}
public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock) {
                if (MyList.size() != 5) {
                    System.out.println("wait begin " + System.currentTimeMillis());
                    lock.wait();
                    System.out.println("wait   end " + System.currentTimeMillis());
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadB extends Thread {
    private Object lock;
    public ThreadB(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add();
                    System.out.println("添加了" + (i + 1) + "个元素");
                    if (MyList.size() == 5) {
                        lock.notify();
                        System.out.println("notify end");
                    }
                    Thread.sleep(1000);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        ThreadA a = new ThreadA(object);
        a.start();
        Thread.sleep(50);
        ThreadB b = new ThreadB(object);
        b.start();
    }
}
wait begin 1588829402319
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
添加了5个元素
notify end
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
wait end 1588829412382

日志wait end…在最后输出,也验证了我们之前的结论:noitfy()方法执行完之后不会立即释放锁。

synchronized可以将任何一个Object对象作为同步对象来对待,而Java为每个Object都实现了wait()notify(),他们必须用在被synchronized同步的Object的临界区内。
wait()可以使处于临界区内的线程进入等待状态,同时释放被同步的对象的锁。
notify()可以唤醒一个因调用了wait()操作而处于阻塞状态中的线程进入就绪状态。被重新唤醒的线程会试图重新获取临界区的控制权。

线程状态切换示意图

1)new Thread()后,调用start(),系统会为此线程分配CPU资源,使其处于Runnable状态,当线程抢占到CPU资源,此线程就处于Running状态。
2)Runnable状态和Runnable可相互切换,线程在Running状态时,调用yield()使当前线程重新回到Runnable状态。
3)Blocked是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

线程进入Runnable状态大致有以下5中情况:
1.调用sleep()方法后经过的时间查过了指定的休眠时间。
2.线程调用的阻塞IO已经返回,阻塞方法执行完毕。
3.线程成功获得视图同步的监视器。
4.线程正在等待某个通知,其他线程发出了通知。
5.处于挂起状态的线程调用了resume()。
线程进入Blocked状态大致有以下5中情况:
1.线程调用sleep(),主动放弃占用的处理器资源。
2.线程调用的阻塞IO方法,在该方法返回前,该线程被阻塞。
3.线程视图获得一个同步监视器,但同步监视器正被其他线程持有。
4.线程等待某个通知。
5.调用suspend()将该线程挂起。

3.1.4 方法wait()锁释放与notify()锁不释放

下面我们进行试验wait锁释放,代码如下:

public class Service {
    public void testMethod(Object lock){
        try {
            synchronized (lock){
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("  end wait()");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class ThreadB extends Thread {
    private Object lock;
    public ThreadB(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class Test {
    public static void main(String[] args) {
        Object lock = new Object();
        ThreadA a = new ThreadA(lock);
        a.start();
        ThreadB b = new ThreadB(lock);
        b.start();
    }
}

执行结果如下:

begin wait()
begin wait()
结论:当wait()被执行后,锁被自动释放。

下面来验证:notify()被执行后不释放锁。

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait() ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
                lock.wait();
                System.out.println("  end wait() ThreadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void synNotifyMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin notify() ThreadName=" + Thread.currentThread().getName() + "  time=" + System.currentTimeMillis() + " time=" + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName=" + Thread.currentThread().getName() + "  time=" + System.currentTimeMillis() + " time=" + System.currentTimeMillis());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class NotifyThread extends Thread {
    private Object lock;
    public NotifyThread(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.synNotifyMethod(lock);
    }
}
public class SynNotifyMethodThread extends Thread {
    private Object lock;
    public SynNotifyMethodThread(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.synNotifyMethod(lock);
    }
}
public class Test {

    public static void main(String[] args) {
        Object lock = new Object();
        ThreadA a = new ThreadA(lock);
        a.start();
        NotifyThread notifyThread = new NotifyThread(lock);
        notifyThread.start();
        SynNotifyMethodThread syn = new SynNotifyMethodThread(lock);
        syn.start();
    }
}
begin wait() ThreadName=Thread-0 time=1590474000435
begin notify() ThreadName=Thread-2  time=1590474000436 time=1590474000436
  end notify() ThreadName=Thread-2  time=1590474005436 time=1590474005436
  end wait() ThreadName=Thread-0 time=1590474005436
begin notify() ThreadName=Thread-1  time=1590474005436 time=1590474005436
  end notify() ThreadName=Thread-1  time=1590474010436 time=1590474010436
结论:必须执行完notify方法所在的同步synchronized代码块之后才释放锁。

3.1.5 当interrupt方法遇到wait方法

当线程呈wait状态时,调用线程对象的interrupt方法时,会出现InterruptException异常。
public class Service {

    public void testMethod(Object lock){
        try {
            synchronized (lock){
                System.out.println("begin wait");
                lock.wait();
                System.out.println("  end wait");
            }
        }catch (Exception e){

        }
    }
}
public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock){
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class Test {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            ThreadA a = new ThreadA(lock);
            a.start();
            Thread.sleep(5000);
            a.interrupt();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
begin wait
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)

结论:
执行同步代码块的过程中,执行锁所属对象的wait()方法后,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒,如果在这个过程中断该线程时,会出现InterruptException异常。

3.1.6 只通知一个线程与通知所有线程

下面来验证:调用notify()时,一次只随机通知一个线程进行唤醒。

public class Service extends Thread {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait ThreadName=" + Thread.currentThread().getName());
                lock.wait();
                System.out.println("  end wait ThreadName=" + Thread.currentThread().getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private Object lock;
    public ThreadA(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class ThreadB extends Thread {
    private Object lock;
    public ThreadB(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class ThreadC extends Thread {
    private Object lock;
    public ThreadC(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}
public class NotifyThread extends Thread {
    private Object lock;
    public NotifyThread(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            lock.notify();
        }
    }
}
public class Test {

    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        ThreadA a = new ThreadA(lock);
        a.start();
        ThreadB b = new ThreadB(lock);
        b.start();
        ThreadC c = new ThreadC(lock);
        c.start();
        Thread.sleep(1000);
        NotifyThread notifyThread = new NotifyThread(lock);
        notifyThread.start();
    }
}
begin wait ThreadName=Thread-0
begin wait ThreadName=Thread-3
begin wait ThreadName=Thread-1
  end wait ThreadName=Thread-0
结论:调用notify方法后,随机通知一个等待该对象的对象锁的其他线程。

那么如何一次性唤醒所有等待该对象的对象锁的其他线程呢?修改NotifyThread.java代码如下:

public class NotifyThread extends Thread {
    private Object lock;
    public NotifyThread(Object lock) {
        this.lock = lock;
    }
    @Override
    public void run() {
        synchronized (lock) {
            lock.notifyAll();
        }
    }
}
begin wait ThreadName=Thread-0
begin wait ThreadName=Thread-1
begin wait ThreadName=Thread-2
  end wait ThreadName=Thread-2
  end wait ThreadName=Thread-1
  end wait ThreadName=Thread-0

3.1.8 方法wait(long)的使用

wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。
public class MyRunnable {
    static private Object lock = new Object();
    static private Runnable runnable = new Runnable() {
        public void run() {
            try {
                synchronized (lock) {
                    System.out.println("wait begin timer=" + System.currentTimeMillis());
                    lock.wait(5000);
                    System.out.println("wait   end timer=" + System.currentTimeMillis());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    static private Runnable runnable2 = new Runnable() {
        public void run() {
            synchronized (lock){
                System.out.println("notify begin timer=" + System.currentTimeMillis());
                lock.notify();
                System.out.println("notify   end timer=" + System.currentTimeMillis());
            }
        }
    };
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(runnable);
        t.start();
    }
}
wait begin timer=1590475975460
wait   end timer=1590475980460

3.1.9 等待wait的条件发生变化。

在使用wait/notify模式时,还需要注意另一正情况,也就是wait的等待条件发生变化,容易造成程序逻辑的混乱。

public class ValueObject {
    public static List<String> list = new ArrayList<String>();
}
public class Add {
    private String lock;
    public Add(String lock) {
        this.lock = lock;
    }
    public void add() {
        synchronized (lock) {
            ValueObject.list.add("lalala");
            lock.notifyAll();
        }
    }
}
public class AddThread extends Thread {
    public Add add;

    public AddThread(Add add) {
        this.add = add;
    }
    @Override
    public void run() {
        add.add();
    }
}
public class Subtract {
    private String lock;

    public Subtract(String lock) {
        this.lock = lock;
    }
    public void subtract() {
        try {
            synchronized (lock) {
                if (ValueObject.list.size() == 0) {
                    System.out.println("wait begin ThreadName=" + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName=" + Thread.currentThread().getName());
                }
                System.out.println("do remove threadName=" + Thread.currentThread().getName());
                ValueObject.list.remove(0);
                System.out.println("do remove end threadName=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class SubtractThread extends Thread {
    public Subtract subtract;
    public SubtractThread(Subtract subtract) {
        this.subtract = subtract;
    }
    @Override
    public void run() {
        subtract.subtract();
    }
}
public class Run {

    public static void main(String[] args) throws InterruptedException {
        String lock = "";
        Add add = new Add(lock);
        Subtract subtract = new Subtract(lock);
        SubtractThread subtractThread = new SubtractThread(subtract);
        subtractThread.setName("sub1");
        subtractThread.start();

        SubtractThread subtractThread2 = new SubtractThread(subtract);
        subtractThread2.setName("sub2");
        subtractThread2.start();
        Thread.sleep(1000);
        AddThread addThread = new AddThread(add);
        addThread.setName("add");
        addThread.start();
    }
}
wait begin ThreadName=sub2
wait begin ThreadName=sub1
wait   end ThreadName=sub1
do remove threadName=sub1
do remove end threadName=sub1
wait   end ThreadName=sub2
do remove threadName=sub2
Exception in thread "sub2" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:657)
	at java.util.ArrayList.remove(ArrayList.java:496)

出现上述问题的原因是Thread.sleep()之前,sub1,sub2线程都都执行了wait方法,进入WAITING状态,在add线程执行完成之后通过notifyAdd()唤醒两个线程,在一个线程执行完remove之后,list的size为零,这时候,另一个线程再进行remove操作的时候就抛出了IndexOutOfBoundsException。
那么下面我们修改Subtract.java代码如下:

public class Subtract {
    private String lock;

    public Subtract(String lock) {
        this.lock = lock;
    }
    public void subtract() {
        try {
            synchronized (lock) {
                while (ValueObject.list.size() == 0) {
                    System.out.println("wait begin ThreadName=" + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName=" + Thread.currentThread().getName());
                }
                System.out.println("do remove threadName=" + Thread.currentThread().getName());
                ValueObject.list.remove(0);
                System.out.println("do remove end threadName=" + Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
wait begin ThreadName=sub1
wait begin ThreadName=sub2
wait   end ThreadName=sub2
do remove threadName=sub2
do remove end threadName=sub2
wait   end ThreadName=sub1
wait begin ThreadName=sub1

通过上面打印信息可以看出,当两个线程同时被唤醒之后,两个SubtractThread线程其中一个执行完remove之后,另一个线程执行完wait()方法之后的代码,因满足ValueObject.list.size() == 0条件,重新进入WAITING状态。

3.1.11 生产者/消费者模式的实现。

等待/通知模式的经典案例就是生产者/消费者模式。

3.1.11.1 一生产与一消费:操作值
public class ValueObject {
    public static String value = "";
}
public class Producer {
    private String lock;
    public Producer(String lock) {
        this.lock = lock;
    }
    public void setValue() {
        try {
            synchronized (lock){
                if (!"".equals(ValueObject.value)) {
                    lock.wait();
                }
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("put value = " + value);
                ValueObject.value = value;
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ProducerThread extends Thread {
    private Producer producer;
    public ProducerThread(Producer producer) {
        this.producer = producer;
    }
    @Override
    public void run() {
        while (true) {
            producer.setValue();
        }
    }
}
public class Consumer {
    private String lock;
    public Consumer(String lock) {
        this.lock = lock;
    }
    public void getValue() {
        synchronized (lock) {
            if ("".equals(ValueObject.value)) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("get Value = " + ValueObject.value);
            ValueObject.value = "";
            lock.notify();
        }
    }
}

public class ConsumerThread extends Thread {
    private Consumer consumer;
    public ConsumerThread(Consumer consumer) {
        this.consumer = consumer;
    }
    @Override
    public void run() {
        while (true) {
            consumer.getValue();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        String lock = "";
        Producer producer = new Producer(lock);
        Consumer consumer = new Consumer(lock);
        ProducerThread producerThread = new ProducerThread(producer);
        ConsumerThread consumerThread = new ConsumerThread(consumer);
        producerThread.start();
        consumerThread.start();
    }
}
put value = 1590487918891_263135745700279
get Value = 1590487918891_263135745700279

put value = 1590487918891_263135745708299
get Value = 1590487918891_263135745708299

put value = 1590487918891_263135745716318
get Value = 1590487918891_263135745716318

put value = 1590487918891_263135745729149
get Value = 1590487918891_263135745729149

put value = 1590487918891_263135745736848
get Value = 1590487918891_263135745736848

put value = 1590487918891_263135745744547
get Value = 1590487918891_263135745744547

···

当前只是一个生产者与一个消费者进行的数据交互。但是如果在此实验的基础上,出现多个生产与消费者,那么在运行的时候极有可能出现“假死”的情况,也就是所有的线程都呈WAITING状态。

3.1.11.2 多生产与多消费:假死
public class Producer {
    private Object lock;
    private List<String> list;
    public Producer(Object lock, List<String> list) {
        this.lock = lock;
        this.list = list;
    }
    public void setValue() {
        try {
            synchronized (lock) {
                if (list.size() != 0) {
                    System.out.println("生产者 :" + Thread.currentThread().getName() + " 处于WAITING");
                    lock.wait();
                }
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                System.out.println("生产者 :" + Thread.currentThread().getName() + " 处于RUNNABLE");
                list.add(value);
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ProducerThread extends Thread {
    private Producer producer;
    public ProducerThread(Producer producer) {
        this.producer = producer;
    }
    @Override
    public void run() {
        while (true) {
            producer.setValue();
        }
    }
}
public class Consumer {
    private Object lock;
    private List<String> list;
    public Consumer(Object lock, List<String> list) {
        this.lock = lock;
        this.list = list;
    }
    public void getValue() {
        try {
            synchronized (lock) {
                if (list.size() == 0) {
                    System.out.println("消费者 :" + Thread.currentThread().getName() + " 处于WAITING");
                    lock.wait();
                }
                System.out.println("消费者 :" + Thread.currentThread().getName() + " 处于RUNNABLE");
                list.remove(0);
                lock.notify();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ConsumerThread extends Thread {
    private Consumer consumer;
    public ConsumerThread(Consumer consumer) {
        this.consumer = consumer;
    }
    @Override
    public void run() {
        while (true) {
            consumer.getValue();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Object lock = new Object();
        List<String> list = new ArrayList<String>();
        Consumer consumer = new Consumer(lock, list);
        Producer producer = new Producer(lock, list);
        ConsumerThread[] consumerThread = new ConsumerThread[2];
        ProducerThread[] producerThread = new ProducerThread[2];
        for (int i = 0; i < 2; i++) {
            consumerThread[i] = new ConsumerThread(consumer);
            consumerThread[i].setName("消费者" + (i + 1));
            producerThread[i] = new ProducerThread(producer);
            producerThread[i].setName("生产者" + (i + 1));
            consumerThread[i].start();
            producerThread[i].start();
        }
        Thread.sleep(5000);
        // Thread.currentThread().getThreadGroup().activeCount() 活动线程数
        Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
        // 每个活动线程的线程组及其子组复制到指定的数组中
        Thread.currentThread().getThreadGroup().enumerate(threadArray);
        for (Thread thread : threadArray) {
            System.out.println(thread.getName() + " " + thread.getState());
        }
    }
}
···
生产者 :生产者2 处于RUNNABLE
生产者 :生产者2 处于WAITING
生产者 :生产者1 处于WAITING
消费者 :消费者1 处于RUNNABLE
消费者 :消费者1 处于WAITING
消费者 :消费者2 处于RUNNABLE
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:657)
	at java.util.ArrayList.remove(ArrayList.java:496)
	at part17.Consumer.getValue(Consumer.java:20)
	at part17.ConsumerThread.run(ConsumerThread.java:11)
消费者 :消费者2 处于WAITING
main RUNNABLE
Monitor Ctrl-Break RUNNABLE
消费者1 WAITING
生产者1 WAITING
消费者2 WAITING
生产者2 WAITING

出现都在等待状态的原因是:代码中确实已经通过notify/wait进行通信了,但不保证notify唤醒的也许是同类(生产者唤醒生产者、消费者唤醒消费者),如果是按照这种情况运行,那么就会造成该情况的出现。解决方案就是使用notifyAll方法即可。

3.1.11.3 一生产与一消费:操作栈

生产者向堆栈List对象中放入数据,使消费者从List堆栈中取出数据。

public class MyStack {
    private List<String> list = new ArrayList();

    public synchronized void push() {
        System.out.println("do push threadName=" + Thread.currentThread().getName());
        try {
            if (list.size() == 1) {
                wait();
            }
            list.add("anyString=" + Math.random());
            notify();
            System.out.println("push size = " + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized String pop() {
        String returnValue = "";
        System.out.println("do pop threadName=" + Thread.currentThread().getName());
        try {
            if (list.size() == 0) {
                System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈WAITING");
                wait();
            }
            returnValue = "" + list.get(0);
            list.remove(0);
            notify();
            System.out.println("pop=" + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}
public class Producer {
    private MyStack myStack;
    public Producer(MyStack myStack) {
        this.myStack = myStack;
    }
    public void pushService() {
        myStack.push();
    }
}
public class ProducerThread extends Thread {
    private Producer producer;
    public ProducerThread(Producer producer) {
        this.producer = producer;
    }
    @Override
    public void run() {
        while (true) {
            producer.pushService();
        }
    }
}
public class Consumer {
    private MyStack myStack;
    public Consumer(MyStack myStack) {
        this.myStack = myStack;
    }
    public void popService() {
        myStack.pop();
    }
}
public class ConsumerThread extends Thread {
    private Consumer consumer;
    public ConsumerThread(Consumer consumer) {
        this.consumer = consumer;
    }
    @Override
    public void run() {
        while (true){
            consumer.popService();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        Producer producer = new Producer(myStack);
        Consumer consumer = new Consumer(myStack);
        ProducerThread producerThread = new ProducerThread(producer);
        ConsumerThread consumerThread = new ConsumerThread(consumer);
        producerThread.start();
        consumerThread.start();
    }
}
···
do push threadName=Thread-0
push size = 1
do push threadName=Thread-0
do pop threadName=Thread-1
pop=0
do pop threadName=Thread-1
pop操作中的:Thread-1线程呈WAITING
push size = 1
do push threadName=Thread-0
pop=0
do pop threadName=Thread-1
pop操作中的:Thread-1线程呈WAITING
push size = 1
do push threadName=Thread-0
pop=0
do pop threadName=Thread-1
···
3.1.11.4 一生产与多消费-操作栈:解决wait条件改变与假死

使用上一节的代码,修改Run方法如下:

public class Run {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        Producer producer = new Producer(myStack);
        Consumer consumer1 = new Consumer(myStack);
        Consumer consumer2 = new Consumer(myStack);
        Consumer consumer3 = new Consumer(myStack);
        Consumer consumer4 = new Consumer(myStack);
        Consumer consumer5 = new Consumer(myStack);
        ProducerThread producerThread = new ProducerThread(producer);
        producerThread.start();
        ConsumerThread consumerThread1 = new ConsumerThread(consumer1);
        ConsumerThread consumerThread2 = new ConsumerThread(consumer2);
        ConsumerThread consumerThread3 = new ConsumerThread(consumer3);
        ConsumerThread consumerThread4 = new ConsumerThread(consumer4);
        ConsumerThread consumerThread5 = new ConsumerThread(consumer5);
        consumerThread1.start();
        consumerThread2.start();
        consumerThread3.start();
        consumerThread4.start();
        consumerThread5.start();
    }
}
do push threadName=Thread-0
push size = 1
do push threadName=Thread-0
do pop threadName=Thread-5
pop=0
do pop threadName=Thread-5
pop操作中的:Thread-5线程呈WAITING
do pop threadName=Thread-4
pop操作中的:Thread-4线程呈WAITING
push size = 1
do push threadName=Thread-0
pop=0
do pop threadName=Thread-5
pop操作中的:Thread-5线程呈WAITING
Exception in thread "Thread-4" do pop threadName=Thread-3
pop操作中的:Thread-3线程呈WAITING
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(ArrayList.java:657)
	at java.util.ArrayList.get(ArrayList.java:433)
	at com.ykc.m.MyStack.pop(MyStack.java:31)
	at com.ykc.m.Consumer.popService(Consumer.java:9)
	at com.ykc.m.ConsumerThread.run(ConsumerThread.java:11)
do pop threadName=Thread-2
pop操作中的:Thread-2线程呈WAITING
do pop threadName=Thread-1
pop操作中的:Thread-1线程呈WAITING

由于条件发生改变没有及时得到响应,所以多个呈wait状态的线程被唤醒,执行remove方法时出现异常,我们可以吧if改成while即可,代码如下:

public class MyStack {
    private List<String> list = new ArrayList();

    public synchronized void push() {
        System.out.println("do push threadName=" + Thread.currentThread().getName());
        try {
            while (list.size() == 1) {
                wait();
            }
            list.add("anyString=" + Math.random());
            notify();
            System.out.println("push size = " + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized String pop() {
        String returnValue = "";
        System.out.println("do pop threadName=" + Thread.currentThread().getName());
        try {
            while (list.size() == 0) {
                System.out.println("pop操作中的:" + Thread.currentThread().getName() + "线程呈WAITING");
                wait();
            }
            returnValue = "" + list.get(0);
            list.remove(0);
            notify();
            System.out.println("pop=" + list.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return returnValue;
    }
}
···
do pop threadName=Thread-5
pop操作中的:Thread-5线程呈WAITING
do pop threadName=Thread-1
pop操作中的:Thread-1线程呈WAITING
do pop threadName=Thread-2
pop操作中的:Thread-2线程呈WAITING
do pop threadName=Thread-4
pop操作中的:Thread-4线程呈WAITING
push size = 1
do push threadName=Thread-0
pop=0
do pop threadName=Thread-3
pop操作中的:Thread-3线程呈WAITING
pop操作中的:Thread-1线程呈WAITING
pop操作中的:Thread-5线程呈WAITING

异常的问题是解决了,却又出现了“假死”,之前我们有验证过,把notify方法,改为notifyAll即可,有疑问的可以重新看3.1.11.2小节。

3.1.11.4 多生产与多消费-操作栈:解决wait条件改变与假死

仍然使用上一章节代码,修改我们的Run.java的代码如下:

public class Run {
    public static void main(String[] args) {
        MyStack myStack = new MyStack();
        Producer producer1 = new Producer(myStack);
        Producer producer2 = new Producer(myStack);
        Producer producer3 = new Producer(myStack);
        Producer producer4 = new Producer(myStack);
        Producer producer5 = new Producer(myStack);
        Consumer consumer1 = new Consumer(myStack);
        Consumer consumer2 = new Consumer(myStack);
        Consumer consumer3 = new Consumer(myStack);
        Consumer consumer4 = new Consumer(myStack);
        Consumer consumer5 = new Consumer(myStack);
        ProducerThread producerThread1 = new ProducerThread(producer1);
        ProducerThread producerThread2 = new ProducerThread(producer2);
        ProducerThread producerThread3 = new ProducerThread(producer3);
        ProducerThread producerThread4 = new ProducerThread(producer4);
        ProducerThread producerThread5 = new ProducerThread(producer5);
        producerThread1.start();
        producerThread2.start();
        producerThread3.start();
        producerThread4.start();
        producerThread5.start();
        ConsumerThread consumerThread1 = new ConsumerThread(consumer1);
        ConsumerThread consumerThread2 = new ConsumerThread(consumer2);
        ConsumerThread consumerThread3 = new ConsumerThread(consumer3);
        ConsumerThread consumerThread4 = new ConsumerThread(consumer4);
        ConsumerThread consumerThread5 = new ConsumerThread(consumer5);
        consumerThread1.start();
        consumerThread2.start();
        consumerThread3.start();
        consumerThread4.start();
        consumerThread5.start();
    }
}
do push threadName=Thread-0
push size = 1
do push threadName=Thread-0
do push threadName=Thread-2
do push threadName=Thread-4
do pop threadName=Thread-8
pop=0
do pop threadName=Thread-8
pop操作中的:Thread-8线程呈WAITING
push size = 1
do push threadName=Thread-4
do pop threadName=Thread-5
pop=0
do pop threadName=Thread-5
pop操作中的:Thread-5线程呈WAITING
push size = 1
do push threadName=Thread-2
pop=0
do pop threadName=Thread-5
pop操作中的:Thread-5线程呈WAITING
push size = 1
do pop threadName=Thread-9
pop=0
···

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

PipeStream是一种特殊的流,用于在不同线程间直接传送数据。一个线程发送数据输出管道,另一个线程从输入管道中读取数据。
1)PipedInputStreamPipedOutputStream
2)PipedReaderPipedWriter

class WriteData {
    void writeMethod(PipedOutputStream out) throws IOException {
        try {
            System.out.println("write");
            for (int i = 0; i < 10; i++) {
                String outData = "" + (i + 1);
                out.write(outData.getBytes());
                System.out.print(outData);
            }
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            out.close();
        }
    }
}
public class WriteThread extends Thread {
    private WriteData write;
    private PipedOutputStream out;
    public WriteThread(WriteData write, PipedOutputStream out) {
        this.write = write;
        this.out = out;
    }
    @Override
    public void run() {
        try {
            write.writeMethod(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class ReadData {
    public void readMethod(PipedInputStream input) throws IOException {
        try {
            System.out.println("read");
            byte[] byteArray = new byte[20];
            int readLength = input.read(byteArray);
            while (readLength != -1) {
                String newData = new String(byteArray, 0, readLength);
                System.out.print(newData);
                readLength = input.read(byteArray);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            input.close();
        }
    }
}
public class ReadThread extends Thread {
    private ReadData read;
    private PipedInputStream in;
    public ReadThread(ReadData read, PipedInputStream in) {
        this.read = read;
        this.in = in;
    }
    @Override
    public void run() {
        try {
            read.readMethod(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) throws IOException, InterruptedException {
        WriteData writeData = new WriteData();
        ReadData readData = new ReadData();
        PipedInputStream inputStream = new PipedInputStream();
        PipedOutputStream outputStream = new PipedOutputStream();
        outputStream.connect(inputStream);
        WriteThread writeThread = new WriteThread(writeData, outputStream);
        writeThread.start();
        Thread.sleep(1000);
        ReadThread readThread = new ReadThread(readData, inputStream);
        readThread.start();
    }
}
write
12345678910
read
12345678910

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

public class WriteData {
    public void writeMethod(PipedWriter out) {
        try {
            System.out.print("write :");
            for (int i = 0; i < 11; i++) {
                String outData = "" + (i + 1);
                System.out.print(outData);
                out.write(outData);
            }
            System.out.println();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class WriteThread extends Thread {
    private WriteData write;
    private PipedWriter out;
    public WriteThread(WriteData write, PipedWriter out) {
        this.write = write;
        this.out = out;
    }
    @Override
    public void run() {
        write.writeMethod(out);
    }
}
public class ReadData {
    public void readMethod(PipedReader input) {
        try {
            System.out.print(" read :");
            char[] byteArray = new char[20];
            int readLength = input.read(byteArray);
            while (readLength != -1) {
                String newData = new String(byteArray, 0, readLength);
                System.out.print(newData);
                readLength = input.read(byteArray);
            }
            input.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
public class ReadThread extends Thread {
    private ReadData read;
    private PipedReader in;
    public ReadThread(ReadData read, PipedReader in) {
        this.read = read;
        this.in = in;
    }
    @Override
    public void run() {
        read.readMethod(in);
    }
}
public class Run {
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();
            PipedReader inputStream = new PipedReader();
            PipedWriter outputStream = new PipedWriter();
            outputStream.connect(inputStream);
            WriteThread writeThread = new WriteThread(writeData, outputStream);
            writeThread.start();
            Thread.sleep(100);
            ReadThread readThread = new ReadThread(readData, inputStream);
            readThread.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
write :1234567891011
 read :1234567891011

3.2 join方法的使用

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

public class MyThread extends Thread {
    @Override
    public void run() {
        int secondValue = (int) (Math.random() * 10000);
        System.out.println(secondValue);
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread threadTest = new MyThread();
        threadTest.start();
        System.out.println("想在线程运行结束后打印···");
    }
}
想在线程运行结束后打印···
9607

修改Test.java代码如下:

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread threadTest = new MyThread();
        threadTest.start();
        threadTest.join();
        System.out.println("想在线程运行结束后打印···");
    }
}
5277
想在线程运行结束后打印···

join()的作用是使所属线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期的阻塞,等待线程x销毁后,再继续执行线程z后面的代码。在join过程中,如果当前线程对象被中断,则当前会出现InterruptedException。

join()具有使线程排队运行的作用,有些类似同步的运行效果。join与synchronized的区别是:join()在内部使用wait()方法进行等待,而synchronized使用的是对象监视器来实现同步。

3.2.1 join(long)与sleep(long)的区别

join(long)的功能在内部是使用wait(long)来实现的,所以join(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;
            }
        }
    }

3.2.2 方法join()后面的代码提前运行

public class ThreadB extends Thread {
    @Override
    synchronized public void run() {
        try {
            System.out.println("begin B threadName=" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("  end B threadName=" + Thread.currentThread().getName() + " " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread {
    private ThreadB threadB;
    public ThreadA(ThreadB threadB) {
        this.threadB = threadB;
    }
    @Override
    public void run() {
        synchronized (threadB) {
            try {
                System.out.println("begin A threadName = " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
                Thread.sleep(5000);
                System.out.println("  end A threadName = " + Thread.currentThread().getName() + " " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Run {
    public static void main(String[] args) {
        try {
            ThreadB threadB = new ThreadB();
            ThreadA threadA = new ThreadA(threadB);
            threadA.start();
            threadB.start();
            threadB.join(2000);
            System.out.println("main end " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
begin A threadName = Thread-1 1590631573130
  end A threadName = Thread-1 1590631578130
main end 1590631578130
begin B threadName=Thread-0 1590631578130
  end B threadName=Thread-0 1590631583130

那么下面我们来分析一下问题出现的原因,修改Run.java代码如下:

public class Run {
    public static void main(String[] args) {
        ThreadB threadB = new ThreadB();
        ThreadA threadA = new ThreadA(threadB);
        threadA.start();
        threadB.start();
        System.out.println("main end " + System.currentTimeMillis());
    }
}
main end 1590631921788
begin A threadName = Thread-1 1590631921788
  end A threadName = Thread-1 1590631926790
begin B threadName=Thread-0 1590631926790
  end B threadName=Thread-0 1590631931790

从执行结果来看,“main end ···”每次都是第一个打印的。那么针对上次的执行结果来看,join(2000)几乎是每次都是先运行的,也就是最先抢到ThreadB的锁,然后释放。基本的过程如下:
1)threadB.join(2000)先抢到ThreadB的锁,然后释放。
2)ThreadA抢到ThreadB的锁,执行synchronized代码块内的代码,进行sleep(5000),5s结束后打印end。
3)join(2000)和ThreadB再次争抢锁,时间已过2000ms,释放锁,然后打印“main end”。
4)ThreadB获得锁之后执行run方法。

3.3 ThreadLocal的使用

ThreadLocal是除了加锁这种同步方式之外,保证多线程访问不会出现非线程安全问题,当我们在创建一个变量后,每个线程对其进行访问的时,访问的都是线程自己的变量这样就不会存在线程不安全问题。
Map里面存储线程本地对象(key)和线程的变量副本(value)
但是,Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。
下面我看几个常用方法如下:

    /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
    	// 获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取当前线程对象的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        // 判断map是否存在
        if (map != null) {
        	// 当前ThreadLocal实例对象为key,获取之前存储的实体
            ThreadLocalMap.Entry e = map.getEntry(this);
            // 判断当前实体是否存在
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        // 若map不存在则进行初始化值
        return setInitialValue();
    }
    
   /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
    	// 返回当前线程的ThreadLocalMap 
        return t.threadLocals;
    }

    /**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    private T setInitialValue() {
    	// null
        T value = initialValue();
        // 获取当前线程
        Thread t = Thread.currentThread();
        // 获取当前线程对象的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	// map不为空时,初始化存储对象实体value为null
            map.set(this, value);
        else
        	// map为空时,以当前线程的threadLocals为key
            createMap(t, value);
        return value;
    }

    protected T initialValue() {
        return null;
    }

   /**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

    /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    /**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * {@code initialValue} method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

注意点:在线程run() 方法中不显示的调用remove() 清理与线程相关的ThreadLocal 信息,线程复用会产生脏数据(线程池会重用Thread对象,如果先一个线程不调用set() 设置初始值,那么与Thread绑定的类静态属性也会被重用)。

3.3.1 验证线程变量的隔离性

public class ThreadLocalTools {
    public static ThreadLocal threadLocal = new ThreadLocal();
}
public class ThreadA extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                ThreadLocalTools.threadLocal.set("ThreadA " + i);
                System.out.println("ThreadA get Value = " + ThreadLocalTools.threadLocal.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadB extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                ThreadLocalTools.threadLocal.set("ThreadB " + i);
                System.out.println("ThreadB get Value = " + ThreadLocalTools.threadLocal.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        ThreadA a = new ThreadA();
        ThreadB b = new ThreadB();
        a.start();
        b.start();
        for (int i = 0; i < 100; i++) {
            ThreadLocalTools.threadLocal.set("main thread " + i);
            System.out.println("main thread getValue " + ThreadLocalTools.threadLocal.get());
            Thread.sleep(200);
        }
    }
}
···
main thread getValue main thread 97
ThreadA get Value = ThreadA 97
ThreadB get Value = ThreadB 97
main thread getValue main thread 98
ThreadA get Value = ThreadA 98
ThreadB get Value = ThreadB 98
main thread getValue main thread 99
ThreadA get Value = ThreadA 99
ThreadB get Value = ThreadB 99

3.4 InheritableThreadLocal的使用

使用InheritableThreadLocal可以再子线程中取得父线程继承下来的值。

public class InheritableThreadLocalExt extends InheritableThreadLocal {
    @Override
    protected Object initialValue() {
        return System.currentTimeMillis();
    }
}
public class Tools {
    public static InheritableThreadLocalExt t1 = new InheritableThreadLocalExt();
}
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("MyThread获取的值 = " + Tools.t1.get());
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println(" main线程取值    = " + Tools.t1.get());
        Thread.sleep(1000);
        MyThread myThread = new MyThread();
        myThread.start();
    }
}
 main线程取值    = 1590647232214
MyThread获取的值 = 1590647232214

参考文献

《Java多线程编程核心技术》高红岩

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生逆旅我亦行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值