Java学习笔记-生产者消费者问题

Java学习笔记-生产者消费者问题

synchronized

我们可以使用synchronized来解决生产者消费者问题,比如当num为0时,使B线程等待,唤醒A线程让其加1,当num为1时使A线程等待,并唤醒B线程让其减1

public class PCSyn{
    public static void main(String[] args){
        // 获取资源对象
        Data data = new Data();
        
        // 让其执行十次
        for(int i = 0; i < 10; i ++){
            // A线程执行增加操作
            new Thread(()->{data.increment();},"A").start();
            
            // B线程执行减操作
            new Thread(()->{data.decrement();},"B").start();
        }
    }
}

class Data{
    
    // 信号量
    private int num = 0;
    
    // 增1
    public synchronized void increment(){
        // 当num不为0时让该线程等待
        if(num != 0)
            this.wait();
        // 执行业务操作
        num ++;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        // 执行完成,唤醒
        notifyAll();
    }
    
    public synchronized void decrement(){
        // 当num为0时让该线程等待
        if(num == 0)
            this.wait();
        // 执行业务操作
        num --;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        // 执行完成,唤醒
        notifyAll();
    }
    
}

到这,在AB两个线程执行时,结果确实是按照我们的预想交替出现01,但是,这样的代码就没有问题了吗?

同样的情况如果我们在上述代码中再加入两个线程CD执行同样的操作,出现的结果会是我们想要的吗?显然,结果并不是

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1cjAxYKg-1622540836997)(C:\Users\10098\AppData\Roaming\Typora\typora-user-images\image-20210601110236722.png)]

那么,为什么会出现这样的情况呢?

我们可以看到,当num因上一次的操作数值发生变化之后,我们进行了if判断,if判断只判断了一次,当这个条件被满足时,比如num此时为0,那么线程A和线程C都被唤醒了,但是其中只有一个线程是有用的,那么另一个就是虚假唤醒,根据官方文档

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eEOJ8qas-1622540837000)(C:\Users\10098\AppData\Roaming\Typora\typora-user-images\image-20210601114128048.png)]

所以,要想解决这个问题,那么只需将我们等待的判断条件处于循环中即可

public class PCSyn{
    public static void main(String[] args){
        // 获取资源对象
        Data data = new Data();
        
        // 让其执行十次
        for(int i = 0; i < 10; i ++){
            // A线程执行增加操作
            new Thread(()->{data.increment();},"A").start();
            
            // B线程执行减操作
            new Thread(()->{data.decrement();},"B").start();
        }
    }
}

class Data{
    
    // 信号量
    private int num = 0;
    
    // 增1
    public synchronized void increment(){
        // 当num不为0时让该线程等待
        while(num != 0)
            this.wait();
        // 执行业务操作
        num ++;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        // 执行完成,唤醒
        notifyAll();
    }
    
    public synchronized void decrement(){
        // 当num为0时让该线程等待
        while(num == 0)
            this.wait();
        // 执行业务操作
        num --;
        System.out.println(Thread.currentThread().getName() + "->" + num);
        // 执行完成,唤醒
        notifyAll();
    }
    
}

Lock

同样的问题我们也可以使用Lock接口去实现

public class PCLock{
    public static void main(String[] args){
        Data data = new Data();
        
        for(int i = 0; i < 10; i ++){
            new Thread(()->{data.increment();},"A").start();
            
            new Thread(()->{data.decrement();},"B").start();
            
            new Thread(()->{data.increment();},"C").start();
            
            new Thread(()->{data.decrement();},"D").start();
        }
    }
}

class Data{
    
    private int num = 0;
    private Lock lock = new ReentrantLock();
    // 和synchronized不同的是,lock的等待和唤醒在condition对象中,分别对应await()和signalAll()
    private Condition condition = lock.newCondition();
    
    public void increment(){
        lock.lock();
        try{
            while(num != 0)
                num ++;
            System.out.println(Thread.currentThread().getName() + "->" + num);
            condition.signalAll();
        } catch(Exception e){
            e.prinyStackTrace();
        } finally{
            lock.unlock();
        }
    }
    
    public void decrement(){
        lock.lock();
        try{
            while(num == 0)
                num --;
            System.out.println(Thread.currentThread().getName() + "->" + num);
            condition.signalAll();
        } catch(Exception e){
            e.prinyStackTrace();
        } finally{
            lock.unlock();
        }
    } 
    
}

但是,明明能用synchronized解决的问题,为什么要引入一个新的技术呢,这个技术肯定有什么不同的、而且比原有的技术优秀的地方。那么在lock的condition中,我们是可以精准的控制是哪个线程被唤醒,哪个线程等待,比如:实现A线程打印A,B线程打印B,C线程打印C,并且让其每一次出现的顺序是A->B->C

public class PrintLock{
    public static void main(String[] args){
        Data data = new Data();
        
        for(int i = 0; i < 10; i ++){
            new Thread(()->{data.printA();},"A").start();
            
            new Thread(()->{data.printB();},"B").start();
            
            new Thread(()->{data.printC();},"C").start();
        }
    }
}

class Data{
    private int num = 1; 	// 1.A线程 2.B线程 3.C线程
    private Lock lock = new ReentrantLock();
    // 通过多个同步监视器精准唤醒或等待某个线程
    private Condition condition1 = lock.newCondition();		
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    
    public void printA(){
        lock.lock();
        try{
            while(num != 1)
                conditino1.await();
            System.out.println(Thread.currentThread().getName() + "->AAA");
            // 唤醒B线程
            condition2.signal();
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            lock.unlock();
        }
    }
    
    public void printB(){
        lock.lock();
        try{
            while(num != 2)
                conditino1.await();
            System.out.println(Thread.currentThread().getName() + "->BBB");
            // 唤醒C线程
            condition3.signal();
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            lock.unlock();
        }
    }
    
    public void printA(){
        lock.lock();
        try{
            while(num != 3)
                conditino1.await();
            System.out.println(Thread.currentThread().getName() + "->CCC");
            // 唤醒A线程
            condition1.signal();
        } catch(Exception e){
            e.printStackTrace();
        } finally{
            lock.unlock();
        }
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值