3.线程间的通信

前记:师夷长技以自强


1.用轮询实现的线程间通信机制

如下:

import java.util.ArrayList;
import java.util.List;

class MyList{
    private List list = new ArrayList();
    public void add(){
        list.add("haha");
    }
    public int size(){
        return list.size();
    }
}

class ThreadA extends Thread{
    volatile private MyList list;

    public ThreadA(MyList list) {
        this.list = list;
    }

    @Override
    public void run() {
        try{
            for (int i = 0; i < 10; i++) {
                list.add();
                System.out.println("添加了"+(i+1)+"个元素");
                Thread.sleep(1000);
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadB extends Thread{
    volatile private MyList list;

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

    @Override
    public void run() {
        try{
            while (true){
                if(list.size()==5){
                    System.out.println("==5,ThreadB exits!!!");
                    throw new InterruptedException();
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
public class Test {
    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();
    }
}

output:
添加了1个元素
添加了2个元素
添加了3个元素
添加了4个元素
添加了5个元素
==5,ThreadB exits!!!
java.lang.InterruptedException
at ThreadB.run(Test.java:48)
添加了6个元素
添加了7个元素
添加了8个元素
添加了9个元素
添加了10个元素
在线程A添加了5个元素后,线程B检测到了符合的条件然后抛出异常退出。然而,在这种情况下线程B是没有让出CPU在等待的。

2.wait和notify

2.1基本使用

wait函数是让当前线程暂停运行,notify函数是使等待当前锁的线程重新获得cpu运行。需要注意的是,wait和notify在被调用之前都要获取到相应的锁,也就是和如下:

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();
        }
    }
}

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 Test {
    public static void main(String[] args) {
        try{
            Object lock = new Object();
            MyThread1 t1 = new MyThread1(lock);
            t1.start();
            Thread.sleep(3000);
            MyThread2 t2 = new MyThread2(lock);
            t2.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

output:
start wait time=1591601206518
start notify time=1591601209523
end notify time=1591601209526
end wait time=1591601209528
可以看到,线程1停止三分钟后被线程2通知后继续运行。

2.2把第一个案例改为wait和notify实现

import java.util.ArrayList;
import java.util.List;

class MyList{
    private static List list = new ArrayList();
    public static void add(){
        list.add("haha");
    }
    public static int size(){
        return list.size();
    }
}

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();
        }
    }
}

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();
                    if (MyList.size()==5){
                        lock.notify();
                        System.out.println("sended message");
                    }
                    System.out.println("add "+(i+1)+"elements");
                    Thread.sleep(1000);
                }
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

public class Test{
    public static void main(String[] args) {
        try{
            Object lock = new Object();
            ThreadA a = new ThreadA(lock);
            a.start();
            Thread.sleep(50);
            ThreadB b = new ThreadB(lock);
            b.start();
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

output:
wait begin 1591604846284
add 1elements
add 2elements
add 3elements
add 4elements
sended message
add 5elements
add 6elements
add 7elements
add 8elements
add 9elements
add 10elements
wait end1591604856392
线程A刚开始获取锁然后调用wait释放锁,线程B得到锁运行,并在添加第5个元素的时候唤醒了线程A,从而得到以上的运行效果。处理notify方法外,还有一个notifyAll方法。前者是仅仅通知等待队列中的一个线程,后者是通知因同一个资源而进入等待队列的进程,然后根据优先级选择。
每个锁对象都有两个队列,一个是就绪队列,一个是阻塞队列。就绪队列存储了将要获得锁的线程,阻塞队列存储了被阻塞的线程。线程被唤醒进入就绪队列准备被执行调度,线程被wait则进入阻塞队列等待下一次的唤醒。

2.3wait释放锁而notify不释放锁

wait被调用后当前线程将释放锁,但是notify将不会释放,线程执行完同步代码块后才释放锁。

2.4 线程调用了wait方法后,再调用interruput方法会出现InterrruptedException异常

class Service{
    public void testMethod(Object lock){
        try {
            synchronized (lock){
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("end wait");
            }
        }catch (InterruptedException e){
            e.printStackTrace();
            System.out.println("wait thread interrupted!!!");
        }
    }
}

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 (InterruptedException e){
            e.printStackTrace();
        }
    }
}

output:
begin wait()
java.lang.InterruptedException
wait thread interrupted!!!
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Object.wait(Object.java:516)
at Service.testMethod(Test.java:6)
at ThreadA.run(Test.java:26)
可见,线程A调用了wait方法后如果执行interrupt方法,就会引发InterruptedException异常。

2.5 notify只通知一个线程

class Service{
    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 (InterruptedException e){
            e.printStackTrace();
        }
    }
}

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);
    }
}

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);
    }
}

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);
    }
}


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();
    }
}

output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
如果在把通知线程的代码改为如下

class NotifyThread extends Thread{
   private Object lock;

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

   @Override
   public void run() {
       synchronized (lock){
           lock.notify();
           lock.notify();
           lock.notify();
           lock.notify();
           lock.notify();
           lock.notify();
       }
   }
}

output:
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1

2.6 notifyAll唤醒所有线程

把上例的通知线程改为如下即可


class NotifyThread extends Thread{
    private Object lock;

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

    @Override
    public void run() {
        synchronized (lock){
            lock.notifyAll();
        }
    }
}

output:
begin wait() ThreadName=Thread-1
begin wait() ThreadName=Thread-0
begin wait() ThreadName=Thread-2
end wait() ThreadName=Thread-1
end wait() ThreadName=Thread-2
end wait() ThreadName=Thread-0

2.7 wait(long)

如果在指定的时间内没有线程唤醒当前线程,则超过这个时间则自动唤醒。

class MyRunnable{
    static private Object lock = new Object();
    static private Runnable runnable1 = new Runnable() {
        @Override
        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 (InterruptedException e){
                e.printStackTrace();
            }
        }
    };

    public static void main(String[] args) {
        Thread thread = new Thread(runnable1);
        thread.start();
    }
}

output:
wait begin timer=1591623207205
wait end timer=1591623212285

2.8wait/notify模式注意点

notify提前如果其他线程的notify比当前线程更早,则notify是无效的。
wait等待的条件发生变化时我们知道wait线程可以被唤醒,也可以超时自动唤醒,那当线程被唤醒后其所等待的添加发生变化时也会引起异常。比如两个线程同时删除一个集合的元素,开始集合为空两个删除线程都阻塞。后来另一个线程往集合放一个元素,会造成两个线程执行删除,而因为只有一个元素则必定会引发异常。

3 生产者消费者模式

3.1当只有一个生产者和消费者时

此时只要notify就可以完成对异类线程的唤醒。如下:

class P {
    private String lock;

    public P(String lock) {
        this.lock = lock;
    }

    public void setValue() {
        try {
            synchronized (lock) {
                while (!ValueObject.value.equals("")) {
                    System.out.println("P "+Thread.currentThread().getName()+" waiting!");
                    lock.wait();
                }
                System.out.println("P "+Thread.currentThread().getName()+" runnable!");
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                ValueObject.value = value;
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class C {
    private String lock;

    public C(String lock) {
        this.lock = lock;
    }

    public void getValue() {
        try {
            synchronized (lock) {
                while (ValueObject.value.equals("")) {
                    System.out.println("C "+Thread.currentThread().getName()+" waiting!");
                    lock.wait();
                }
                System.out.println("C "+Thread.currentThread().getName()+" runnable!");
                ValueObject.value = "";
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ValueObject {
    public static String value = "";
}

class ThreadP extends Thread {
    private P p;

    public ThreadP(P p) {
        this.p = p;
    }

    @Override
    public void run() {
        while (true) {
            p.setValue();
        }
    }
}

class ThreadC extends Thread {
    private C r;

    public ThreadC(C r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.getValue();
        }
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        String lock = new String("");
        P p = new P(lock);
        C c = new C(lock);
        ThreadP[] pThread = new ThreadP[2];
        ThreadC[] cThread = new ThreadC[2];
        for (int i = 0; i < 1; i++) {
            pThread[i] = new ThreadP(p);
            pThread[i].setName("P"+(i+1));
            cThread[i] = new ThreadC(c);
            cThread[i].setName("C"+(i+1));
            pThread[i].start();
            cThread[i].start();
        }
        Thread.sleep(5000);
        Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
        Thread.currentThread().getThreadGroup().enumerate(threadArray);
        for (int i = 0; i < threadArray.length; i++) {
            System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
        }
    }
}

output:(部分)
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
P P1 runnable!
P P1 waiting!
C C1 runnable!
可以看出,生产者和消费者可以同步进行。

3.2多生产者和多消费者时

如果每次仅仅唤醒一个线程,那么可能只唤醒了同类线程,等待的线程越来越多,最后造成程序的假死。

class P {
    private String lock;

    public P(String lock) {
        this.lock = lock;
    }

    public void setValue() {
        try {
            synchronized (lock) {
                while (!ValueObject.value.equals("")) {
                    System.out.println("P "+Thread.currentThread().getName()+" waiting!");
                    lock.wait();
                }
                System.out.println("P "+Thread.currentThread().getName()+" runnable!");
                String value = System.currentTimeMillis() + "_" + System.nanoTime();
                ValueObject.value = value;
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class C {
    private String lock;

    public C(String lock) {
        this.lock = lock;
    }

    public void getValue() {
        try {
            synchronized (lock) {
                while (ValueObject.value.equals("")) {
                    System.out.println("C "+Thread.currentThread().getName()+" waiting!");
                    lock.wait();
                }
                System.out.println("C "+Thread.currentThread().getName()+" runnable!");
                ValueObject.value = "";
                lock.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ValueObject {
    public static String value = "";
}

class ThreadP extends Thread {
    private P p;

    public ThreadP(P p) {
        this.p = p;
    }

    @Override
    public void run() {
        while (true) {
            p.setValue();
        }
    }
}

class ThreadC extends Thread {
    private C r;

    public ThreadC(C r) {
        this.r = r;
    }

    @Override
    public void run() {
        while (true) {
            r.getValue();
        }
    }
}

public class Test {
    public static void main(String[] args) throws InterruptedException {
        String lock = new String("");
        P p = new P(lock);
        C c = new C(lock);
        ThreadP[] pThread = new ThreadP[2];
        ThreadC[] cThread = new ThreadC[2];
        for (int i = 0; i < 2; i++) {
            pThread[i] = new ThreadP(p);
            pThread[i].setName("P"+(i+1));
            cThread[i] = new ThreadC(c);
            cThread[i].setName("C"+(i+1));
            pThread[i].start();
            cThread[i].start();
        }
        Thread.sleep(5000);
        Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
        Thread.currentThread().getThreadGroup().enumerate(threadArray);
        for (int i = 0; i < threadArray.length; i++) {
            System.out.println(threadArray[i].getName()+" "+threadArray[i].getState());
        }
    }
}

output:(部分)
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
P P1 waiting!
C C2 runnable!
C C2 waiting!
P P1 runnable!
C C2 runnable!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C1 runnable!
C C1 waiting!
P P1 runnable!
P P1 waiting!
P P2 waiting!
C C2 runnable!
C C2 waiting!
C C1 waiting!
main RUNNABLE
Monitor Ctrl-Break RUNNABLE
P1 WAITING
C1 WAITING
P2 WAITING
C2 WAITING
经过若干次的调用,最后所有的生产者和消费者都进入了等待状态。解决的办法也很简单,就是把P.java和C.java中的notify改为notifyAll。

4.管道通信

4.1字节流

import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

class WriteData{
    public void writeMethod(PipedOutputStream out){
        try{
            System.out.println("write :");
            for (int i = 0; i < 300; i++) {
                String outData = "" + (i+1);
                out.write(outData.getBytes());
                System.out.print(outData);
            }
            System.out.println();
            out.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class ReadData{
    public void readMethod(PipedInputStream input){
        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);
            }
            System.out.println();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ThreadWrite extends Thread{
    private WriteData write;
    private PipedOutputStream out;

    public ThreadWrite(WriteData write, PipedOutputStream out) {
        this.write = write;
        this.out = out;
    }

    @Override
    public void run() {
        write.writeMethod(out);
    }
}

class ThreadRead extends Thread{
    private ReadData read;
    private PipedInputStream input;

    public ThreadRead(ReadData read, PipedInputStream input) {
        this.read = read;
        this.input = input;
    }

    @Override
    public void run() {
        read.readMethod(input);
    }
}

public class Test{
    public static void main(String[] args) {
        try {
            WriteData writeData = new WriteData();
            ReadData readData = new ReadData();

            PipedInputStream inputStream = new PipedInputStream();
            PipedOutputStream outputStream = new PipedOutputStream();

//            outputStream.connect(inputStream);
            inputStream.connect(outputStream);

            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            threadRead.start();

            Thread.sleep(2000);

            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            threadWrite.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.2字符流

import java.io.*;

class WriteData{
    public void writeMethod(PipedWriter out){
        try{
            System.out.println("write :");
            for (int i = 0; i < 300; i++) {
                String outData = "" + (i+1);
                out.write(outData);
                System.out.print(outData);
            }
            System.out.println();
            out.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class ReadData{
    public void readMethod(PipedReader input){
        try{
            System.out.println("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);
            }
            System.out.println();
            input.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ThreadWrite extends Thread{
    private WriteData write;
    private PipedWriter out;

    public ThreadWrite(WriteData write, PipedWriter out) {
        this.write = write;
        this.out = out;
    }

    @Override
    public void run() {
        write.writeMethod(out);
    }
}

class ThreadRead extends Thread{
    private ReadData read;
    private PipedReader input;

    public ThreadRead(ReadData read, PipedReader input) {
        this.read = read;
        this.input = input;
    }

    @Override
    public void run() {
        read.readMethod(input);
    }
}

public class Test{
    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);
            inputStream.connect(outputStream);

            ThreadRead threadRead = new ThreadRead(readData, inputStream);
            threadRead.start();

            Thread.sleep(2000);

            ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
            threadWrite.start();

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

5.join方法

5.1join方法的基本使用

join方法使所属的线程对象x正常执行run方法中的任务,使当前线程z进行无限期阻塞,等待线程x销毁后再继续执行线程z后面的代码。

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 Test{
    public static void main(String[] args) {
        try {
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join();
            System.out.println("haha");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
4603
haha

5.2 join方法与interrrupt

class ThreadA extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            String newString = new String();
            Math.random();
        }
    }
}

class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            ThreadA threadA = new ThreadA();
            threadA.start();
            threadA.join();
            System.out.println("ThreadB run end!");
        } catch (InterruptedException e) {
            System.out.println("ThreadB run catch!");
            e.printStackTrace();
        }
    }
}

class ThreadC extends Thread{
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        threadB.interrupt();
    }
}

public class Test{
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            b.start();
            Thread.sleep(500);
            ThreadC c = new ThreadC(b);
            c.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
ThreadB run catch!
java.lang.InterruptedException
at java.base/java.lang.Object.wait(Native Method)
at java.base/java.lang.Thread.join(Thread.java:1353)
at java.base/java.lang.Thread.join(Thread.java:1427)
at ThreadB.run(Test.java:17)
当线程B在调用了join后被线程C interrupt,会发生异常,但线程按钮还是呈“红色”,因为线程A还在继续运行中,没有发生异常。

5.3 join(long)方法

class MyThread extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("begin Timer="+System.currentTimeMillis());
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Test{
    public static void main(String[] args) {
        try {
            MyThread threadTest = new MyThread();
            threadTest.start();
            threadTest.join(2000);
            System.out.println("    end timer="+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
begin Timer=1591694019386
end timer=1591694021385
可以看到,运行的效果是等待了2秒钟。

5.4join(long)和sleep(join)的区别

阅读join的源码发现里面是使用wait实现的,因此join别调用后是可以释放锁的。而sleep方法被调用后却不释放锁。


class ThreadA extends Thread{
    private ThreadB b;

    public ThreadA(ThreadB b) {
        this.b = b;
    }

    @Override
    public void run() {
        try {
            synchronized (b){
                b.start();
                Thread.sleep(6000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("    b run begin timer="+System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("    b run end timer="+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void bService(){
        System.out.println("bService timer"+System.currentTimeMillis());
    }
}

class ThreadC extends Thread{
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {

        threadB.bService();
    }
}


public class Test{
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            ThreadA a = new ThreadA(b);
            a.start();
            Thread.sleep(1000);
            ThreadC c = new ThreadC(b);
            c.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
b run begin timer=1591706030617
b run end timer=1591706035720
bService timer1591706036618
可见,bService是过了6秒后被调用的,也就是说线程A在调用了Thread.sleep(6000)后没有释放线程B对象的锁,导致其同步方法bService不能被线程C立即调用。而join是可以释放锁的,如下


class ThreadA extends Thread{
    private ThreadB b;

    public ThreadA(ThreadB b) {
        this.b = b;
    }

    @Override
    public void run() {
        try {
            synchronized (b){
                b.start();
                b.join();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("    b run begin timer="+System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("    b run end timer="+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public void bService(){
        System.out.println("bService timer"+System.currentTimeMillis());
    }
}

class ThreadC extends Thread{
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {

        threadB.bService();
    }
}


public class Test{
    public static void main(String[] args) {
        try {
            ThreadB b = new ThreadB();
            ThreadA a = new ThreadA(b);
            a.start();
            Thread.sleep(1000);
            ThreadC c = new ThreadC(b);
            c.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
b run begin timer=1591706475496
bService timer1591706476497
b run end timer=1591706480580

6.ThreadLocal使用

ThreadLocal类主要解决每个线程绑定自己的值,变量在不同线程间的隔离性。

public class Test{
    public static ThreadLocal t1 = new ThreadLocal();

    public static void main(String[] args) {
        if(t1.get() == null){
            System.out.println("no value!");
            t1.set("my value");
        }
        System.out.println(t1.get());
        System.out.println(t1.get());
    }
}

output:
no value!
my value
my value
可以通过set和get对ThreadLocal操作。

6.1 验证线程变量的隔离性

class Tools{
    public static ThreadLocal t1 = new ThreadLocal();
}

class ThreadA extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("ThreadA"+i);
                System.out.println("ThreadA get Value="+Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("ThreadB"+i);
                System.out.println("ThreadB get Value="+Tools.t1.get());
                Thread.sleep(200);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test{
    public static void main(String[] args) {
        try {
            ThreadA a = new ThreadA();
            ThreadB b = new ThreadB();
            a.start();
            b.start();
            for (int i = 0; i < 100; i++) {
                Tools.t1.set("Main"+i);
                System.out.println("Main get Value="+Tools.t1.get());
                Thread.sleep(200);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:(部分)
Main get Value=Main92
ThreadA get Value=ThreadA93
ThreadB get Value=ThreadB93
Main get Value=Main93
ThreadA get Value=ThreadA94
ThreadB get Value=ThreadB94
Main get Value=Main94
ThreadA get Value=ThreadA95
ThreadB get Value=ThreadB95
Main get Value=Main95
ThreadA get Value=ThreadA96
ThreadB get Value=ThreadB96
Main get Value=Main96
ThreadA get Value=ThreadA97
ThreadB get Value=ThreadB97
Main get Value=Main97
ThreadA get Value=ThreadA98
ThreadB get Value=ThreadB98
Main get Value=Main98
ThreadA get Value=ThreadA99
ThreadB get Value=ThreadB99
Main get Value=Main99
可见,这三个线程都能取出属于自己的值,ThreadLocal类对每个线程的存储是具有隔离性的。

6.2解决ThreadLocal中值为null

可以创建一个继承自ThreadLocal的类ThreadLocalEx,重写其initialValue方法

import java.util.Date;

class ThreadLocalEx extends ThreadLocal{
    @Override
    protected Object initialValue() {
        return new Date().getTime();
    }
}

class Tools{
    public static ThreadLocalEx t1 = new ThreadLocalEx();
}

class ThreadA extends Thread{
    @Override
    public void run() {
        try{
            for (int i = 0; i < 10; i++) {
                System.out.println("ThreadA value="+Tools.t1.get());
                Thread.sleep(100);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Test{
    public static void main(String[] args) {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("Main value="+Tools.t1.get());
                Thread.sleep(100);
            }
            Thread.sleep(5000);
            ThreadA a = new ThreadA();
            a.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

output:
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
Main value=1591710156676
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
ThreadA value=1591710162774
可以看到,Main和ThreadA中都有自己的默认值了。

7.InheritableThreadLocal类

该类用法与ThreadLocal类似,只是子线程可以继承从父线程中的值。

8.总结

本文主要说明了线程之间是如何通信的,其中等待通知是基本的模式,还有生产者消费者,管道等通信方式,join方法的使用等。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值