线程间通信(生产者消费者模型)


线程间通信

在这里插入图片描述

生产者消费者复习

package cduck.cn;

/**
 * 题日:现在两个线程,可以操作初始值为零的一个交量,
 * 实现一个线程对该变量加,一个线程对该变量减1,
 * 实现交替,来10轮,变量初始值为0。
 *
 * 1、高内聚低耦合前提下,线程操作资源类
 * 2、判断/干活/通知
 * 3、防止虚假唤醒,只能用WHILE循环
 */
class SharingResource{//资源类
    private  int num=0;

    public synchronized void increment() throws InterruptedException {
        //判断
        if (num!=0){
            this.wait();
        }
        //干活
        num++;
        System.out.println(Thread.currentThread().getName()+"\t"+num);
        //通知
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        //判断
        if (num==0){
            this.wait();
        }
        //干活
        num--;
        System.out.println(Thread.currentThread().getName()+"\t"+num);
        //通知
        this.notifyAll();
    }
}


public class WaitNotify {

    public static void main(String[] args) {

        SharingResource SR=new SharingResource();

        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();


        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();


        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }


}

发生问题

以上的例子在两个线程的时候运行正常。但 当有四个线程,两个加两个减就可能出现错误:多加了几次。

在这里插入图片描述

解决

首先来看一下问题原因:

换成4个线程会导致错误,虚假唤醒 原因:在java多线程判断时,不能用if,程序出事出在了判断上面,突然有一添加的线程进到if了,突然中断了交出控制权,没有进行验证,而是直接走下去了,加了两次,甚至多次

sleep():睡的时候 执行权仍然握在自己手里,醒了继续运行
wait():睡的时候就把执行权让出去了

问题发生的图解
在这里插入图片描述


解决办法:

解决虚假唤醒:查看API,java.lang.Object 中断和虚假唤醒是可能产生的,所以要用loop循环,if只判断一次,while是只要唤醒就要拉回来再判断一次。if换成while

代码

class SharingResource{//资源类
    private  int num=0;

    public synchronized void increment() throws InterruptedException {
        //判断
        while (num!=0){
            this.wait();
        }
        //干活
        num++;
        System.out.println(Thread.currentThread().getName()+"\t"+num);
        //通知
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        //判断
        while (num==0){
            this.wait();
        }
        //干活
        num--;
        System.out.println(Thread.currentThread().getName()+"\t"+num);
        //通知
        this.notifyAll();
    }
}

生产者-消费者模型 新实现

用了Condition的await();与signalAll();来代替wait();与notifyAll();

package cduck.cn;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 题日:现在两个线程,可以操作初始值为零的一个交量,
 * 实现一个线程对该变量加,一个线程对该变量减1,
 * 实现交替,来10轮,变量初始值为0。
 *
 * 1、高内聚低耦合前提下,线程操作资源类
 * 2、判断/干活/通知
 */
class SharingResource1{//资源类
    private  int num=0;

    Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();

    public  void increment()  {
        lock.lock();
               try{
                   //判断
                   while (num!=0){
                       condition.await();
                   }
                   //干活
                   num++;
                   System.out.println(Thread.currentThread().getName()+"\t"+num);
                   //通知
                  condition.signalAll();
               }catch (Exception e){

               }finally {
                   lock.unlock();
               }

    }


    public synchronized void decrement() throws InterruptedException {
        lock.lock();
                try{
                    //判断
                    while (num==0){
                        condition.await();
                    }
                    //干活
                    num--;
                    System.out.println(Thread.currentThread().getName()+"\t"+num);
                    //通知
                    condition.signalAll();
                }catch (Exception e){

                }finally {
                    lock.unlock();
                }
    }
}


public class NewProductiveConsumption {

    public static void main(String[] args) {

        SharingResource1 SR1=new SharingResource1();

        new Thread(()->{
            for (int i=0;i<10;i++){
                SR1.increment();
            }
        },"A").start();

        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR1.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();


        new Thread(()->{
            for (int i=0;i<10;i++){
                SR1.increment();
            }
        },"C").start();


        new Thread(()->{
            for (int i=0;i<10;i++){
                try {
                    SR1.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }


}

精准调度

1、有顺序通知,需要有标识位
2、有一个锁Lock,3把钥匙Condition
3、判断标志位
4、输出线程名+第几次+第几轮
5、修改标志位,通知下一个

package cduck.cn;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程之间按顺序调用,实项A->B->C
 * 3个线程启动,要求如下
 * AA打55次,BB打印10次,CC打印15次
 * 接着
 * AA打5次,BB打印10次,CC打印15次
 * ......
 * 来10轮
 *
 * ----------------
 * 主要知识点:根据标志位来精确唤醒
 */

class ShareResource{
    int flag=1;//标志位。1代表A 2代表B  3代表C

    Lock lock=new ReentrantLock();
    Condition c1=lock.newCondition();
    Condition c2=lock.newCondition();
    Condition c3=lock.newCondition();

    public  void  Print5(){
       lock.lock();
               try{
                  //判断
                   while (flag!=1){
                       c1.await();
                   }
                   //干活
                   for (int i=0;i<5;i++){
                       System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
                   }
                   //通知
                   flag=2;//A执行完了,把标志为切换为2
                   c2.signal();//由B的condition 去唤醒B

               }catch (Exception e){

               }finally {
                   lock.unlock();
               }

    }
    public  void  Print10(){
        lock.lock();
        try{
            //判断
            while (flag!=2){
                c2.await();
            }
            //干活
            for (int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
            }
            //通知
            flag=3;//B执行完了,把标志为切换为3
            c3.signal();//由C的condition 去唤醒C

        }catch (Exception e){

        }finally {
            lock.unlock();
        }

    }
    public  void  Print15(){
        lock.lock();
        try{
            //判断
            while (flag!=3){
                c3.await();
            }
            //干活
            for (int i=0;i<15;i++){
                System.out.println(Thread.currentThread().getName()+"\t"+(i+1));
            }
            //通知
            flag=1;//C执行完了,把标志为切换为1
            c1.signal();//由A的condition 去唤醒A

        }catch (Exception e){

        }finally {
            lock.unlock();
        }

    }

}

public class ThreadOrderAccess {

    public static void main(String[] args) {
    ShareResource sr=new ShareResource();
        new Thread(()->{
            for (int i=0;i<10;i++){
                sr.Print5();
            }
        },"A").start();

        new Thread(()->{
            for (int i=0;i<10;i++){
                sr.Print10();
            }
        },"B").start();

        new Thread(()->{
            for (int i=0;i<10;i++){
                sr.Print15();
            }
        },"C").start();
    }
}

文章作者: fFee-ops
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 fFee-ops !
评论
  目录