生产者/消费者问题

流程图

读输入写输出
生产者消费者

分析

  1. 多线程编程stop方法不安全
  2. 常见使用interrupt来中断线程,它的应用场景
    2.1 线程处于阻塞状态,如使用了sleep
    2.2 使用while(!isInterrupted()){……}来判断线程是否被中断
  3. java每个对象有且只有一个内置的同步锁
  4. wait会释放锁,notify仅仅只是通知,不释放锁,同理sleep也不释放锁

notify()或者notifyAll()方法并不是真正释放锁,必须等到synchronized方法或者语法块执行完才真正释放锁

码上见

public class ProducerConsumerTest {
    public static void main(String[] args) {
        CubbyHole c = new CubbyHole();
        Producer p1 = new Producer(c, 1);
        Consumer c1 = new Consumer(c, 1);
        p1.start();
        c1.start();
    }
}

class CubbyHole {
    private int contents;
    private boolean avaliable = false;

    public synchronized int get() {
        while (avaliable == false) {
            try {
                wait();
            } catch (InterruptedException e) {

            }
        }
        avaliable = false;
        notifyAll();
        return contents;
    }

    public synchronized void put(int value) {
        while (avaliable == true) {
            try {
                wait();
            } catch (InterruptedException e) {

            }
        }
        contents = value;
        avaliable = true;
        notifyAll();
    }
}

class Consumer extends Thread {
    private CubbyHole cubbyHole;
    private int number;

    public Consumer(CubbyHole c, int number) {
        cubbyHole = c;
        this.number = number;
    }

    @Override
    public void run() {
        int value = 0;
        for (int i = 0; i < 10; i++) {
            value = cubbyHole.get();
            System.out.println("消费者 #" + this.number + " got: " + value);
        }
    }
}

class Producer extends Thread{
    private CubbyHole cubbyhole;
    private int number;

    public Producer(CubbyHole c, int number){
        cubbyhole = c;
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            cubbyhole.put(i);
            System.out.println("生产者 #"+this.number + "put: "+i);
            try {
                sleep((int)(Math.random()*100));
            } catch (InterruptedException  e) {

            }
        }
    }
}

锁了谁

  1. 锁只有一把,可锁类对象,实例对象,实质只有一把,临界资源,共同操作的只有一段
  2. java用锁主要有三种方式

synchronized 同步代码块机制
Lock 显性锁机制
semphore 信号量,可访问自身的线程数,有尝试,有超时设置

用while作判断后wait

没错就是为了占用,wait会让当前共用资源this进入等待队列,但wait不占用当前对象锁。换而言之获取锁,等待阻塞同时释放锁。

就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait

用while主因:确保同步代码块在其它线程唤醒之后,仍能依据同样条件重新进行。换而言之,条件表达式在线程唤醒前后可能条件结果不一致,用while是为了保证在同一条件值下进入wait待入队列。
wait前后,if 判断条件表达式结果可能不一样(尤其条件计算涉及到共享资源的变量),唤醒之后条件变量产生了变化 ,它只会执行后续代码,会产生虚假唤醒现象。

例如 if xxx {wait; do} 有三个线程A,B,C使用同步代码块等待。 若A先抢到锁,xx为真时 等待,do这个事情本意是在真的情况下继续。在A等待时,B抢到了锁,然后C抢锁等待,假定这时B被换醒得锁,它继续do的,但do干了一件事,将xxx改为了假,然后A被唤醒继续执行,本来A…是要在xxx为真时做的事情,现在好了用if它直接跳过了条件。
用while确定唤醒前后的条件一致性,即确保时刻会进行条件计算,唤醒后仍然受唤醒前同样的条件约束。这种情况,一般是在后续代码对同一共享资源有依赖。在代码中即是对 avaliable 同一状态,才进行下一步操作。

简而言之,用if极大可能会产生唤醒后,条件状态失控。

代码执行

D:\code-base\java\java_learn>cd "d:\code-base\java\java_learn\" && javac -encoding utf-8 ProducerConsumerTest.java && java ProducerConsumerTest
生产者 #1put: 0
消费者 #1 got: 0
生产者 #1put: 1
消费者 #1 got: 1
生产者 #1put: 2
消费者 #1 got: 2
生产者 #1put: 3
消费者 #1 got: 3
生产者 #1put: 4
消费者 #1 got: 4
生产者 #1put: 5
消费者 #1 got: 5
生产者 #1put: 6
消费者 #1 got: 6
生产者 #1put: 7
消费者 #1 got: 7
生产者 #1put: 8
消费者 #1 got: 8
生产者 #1put: 9
消费者 #1 got: 9

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值