提到线程通信,就不得不提经典的生产者消费者模式

1. 生产者消费者模式

实现双线程交替打印 0-1

public class ConProMode {    public static void main(String[] args) {        source s = new source();        new Thread(()-> {   // 生产者线程            for(int i=0; i<10; i++){                try {                s.increase();            } catch (InterruptedException e) {                e.printStackTrace();            }}        },"生产者A").start();        new Thread(()-> {   // 消费者线程            for(int i=0; i<10; i++){            try {                s.decrease();            } catch (InterruptedException e) {                e.printStackTrace();            }}        },"消费者A").start();   }}
class source{   // 资源类    private int num = 0;    public synchronized void increase() throws InterruptedException {          // 1.判断         if(num != 0){   // 此处应该用while            this.wait();        }        // 2.外围业务        num++;        System.out.println(Thread.currentThread().getName() + "\t" + num);        // 3.线程通信        this.notifyAll();    }    public synchronized void decrease() throws InterruptedException {         if(num == 0){               this.wait();        }        num--;        System.out.println(Thread.currentThread().getName() + "\t" + num);        this.notifyAll();    }}

附上最近学习对我了解和编写多线程晋升最大的几句话:

  • 高内聚低耦合,线程操控资源类
  • 操控资源办法集成在资源类中,给线程调用

2. 线程间的虚伪唤醒景象

在1中的生产者消费者模式中,因为生产者和消费者都只有一个,因而不会呈现虚伪唤醒问题;而如果将生产者和消费者都设置为为两个,即共4条线程操控同一份资源且彼此有交互(即通信),则将会呈现线程的虚伪唤醒景象,机制可见下图:

起因是在线程被唤醒后,如果应用if判断的话,因为线程wait前曾经通过一次if判断,而if语句是一个一次性判断,线程昏迷后不会再次执行判断,而是间接执行前面的业务语句,这样就可能导致本该同步的操作却失去了同步性能。因而,在多线程的程序中,控制线程状态的判断语句肯定要应用while,实现反复判断,防止虚伪唤醒的问题

3. Lock实现线程的精准通信

Syncronized同步监视器是一种偏心的锁机制,即当线程开释锁后,监视器上的所有线程都有权力去抢夺这把锁并获取执行权,即线程的执行是无序的(除非有且仅有两条线程,能够交替执行),但很多理论利用场景所须要的线程之间的交互是非偏心的,而是有序的,这就须要监视器为其上的每条线程特备一个认证操作通道。艰深了解为锁对象为每条线程都装备一把钥匙,各线程能够在本人的工作中抉择和特定的线程进行通信,以此达到让线程按指定程序执行。Lock锁便实现了这种机制。

举例:

/** * A - B - C三个线程 * 实现要求:AA间断打印1次  而后BB间断打印2次  而后CC间断打印3次 * 反复5轮 */public class ConProDemo03 {    public static void main(String[] args) {        source03 s = new source03();        new Thread(()->{ for(int i=0; i<5; i++) s.printA(); },"线程A").start();        new Thread(()->{ for(int i=0; i<5; i++) s.printB(); },"线程B").start();        new Thread(()->{ for(int i=0; i<5; i++) s.printC(); },"线程C").start();    }}class source03{    private int flag = 1;    private Lock lock = new ReentrantLock();    private Condition condition1 = lock.newCondition(); // 为线程A配一把钥匙    private Condition condition2 = lock.newCondition(); // 为线程B配一把钥匙    private Condition condition3 = lock.newCondition(); // 为线程C配一把钥匙    public void printA() {        lock.lock();        try{            while(flag != 1){                condition1.await();            }            for(int i=0; i<1; i++){                System.out.println(Thread.currentThread().getName() + "\t" + "AA");            }            flag = 2;            condition2.signal();  // 实现对线程的精准唤醒        }catch (InterruptedException e){            e.printStackTrace();        }finally {            lock.unlock();        }    }    public void printB() {        lock.lock();        try{            while(flag != 2){                condition2.await();            }            for(int i=0; i<2; i++){                System.out.println(Thread.currentThread().getName() + "\t" + "BB");            }            flag = 3;            condition3.signal();        }catch (InterruptedException e){            e.printStackTrace();        }finally {            lock.unlock();        }    }    public void printC() {        lock.lock();        try{            while(flag != 3){                condition3.await();            }            for(int i=0; i<3; i++){                System.out.println(Thread.currentThread().getName() + "\t" + "CC");            }            flag = 1;            condition1.signal();        }catch (InterruptedException e){            e.printStackTrace();        }finally {            lock.unlock();        }    }}