面试遇到的等待通知

in java并发 with 0 comment

用多线程实现一个生产者和消费者模式(不允许使用阻塞队列),一个线程put范围是1-100的数字共计20个,

另外一个线程负责get数字并进行累加,并打印sum之后的结果

面试遇到的。今天突然想起来,就完善一下吧。

生产者

public class Product implements Runnable {
    private List list;
    private Lock lock;
    private Condition condition;
    private int num = 0;
    private int count;

    public Product(List list, Lock lock, Condition condition , int count) {
        this.list = list;
        this.lock = lock;
        this.condition = condition;
        this.count = count;
    }

    @Override
    public void run() {
        try {
            while (num<=count) {
                Random r = new Random();
                // 锁
                lock.lock();
                // 如果list长度大于0,则停止生产,即开始等待
                while (list.size() > 0) {
                    try {
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    // 模拟生产时间
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // add一个随机数
                list.add(r.nextInt(100));
                System.out.println(Thread.currentThread().getName() + (num+1)+"线程生产了" + list.get(0));
                // 通知其他线程
                num++;
                condition.signalAll();

            }
        }finally {
            // 释放锁
            lock.unlock();
        }
    }
}

消费者

public class Consumer implements Runnable {
    private List list;
    private Lock lock;
    private Condition condition;
    private CountDownLatch countDownLatch;
    private int num = 0;

    public int getNum() {
        return num;
    }

    public Consumer(List list, Lock lock, Condition condition , CountDownLatch countDownLatch) {
        this.list = list;
        this.lock = lock;
        this.condition=condition;
        this.countDownLatch=countDownLatch;
    }
    @Override
    public void run() {
        try {
            while (true) {
                // 锁
                lock.lock();
                // 如果list长度小于等于0,则等待通知方通知
                while (list.size() <= 0) {
                    try {
                        // 等待
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    // 模拟消费时间
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 输出
                num=num+(int)list.get(0);
                System.err.println("当前结果为:"+num);
                countDownLatch.countDown();
                list.remove(0);
                // 通知 通知方继续生产
                condition.signalAll();
            }
        }finally {
            // 释放锁
            lock.unlock();
        }
    }
}

main方法

public class Main {
    public static void main(String[] args) {
        int num =20;
        CountDownLatch downLatch = new CountDownLatch(num);
        Lock lock = new ReentrantLock();
        // 线程安全的List 其内部是一个ReentrantLock
        List list = new CopyOnWriteArrayList<Integer>();
        Condition condition = lock.newCondition();
        Consumer consumer = new Consumer(list, lock, condition,downLatch);
        Product product = new Product(list, lock, condition,num);
        try {
            lock.lock();
            new Thread(product,"product").start();
            new Thread(consumer,"consumer").start();
        }finally {
            lock.unlock();
        }
        new Thread(()->{
            try {
                downLatch.await();
                System.out.println("最后值为:"+consumer.getNum());
            } catch (InterruptedException e) {
            }
        }).start();
    }
}

运行结果

image.png
最后运行了几次,生产者最后生产的一个都没被消费。暂时还没找到原因,为了消费20个,就设置的生产条件是num<=count。。。

问题解决

续上。和朋友交流了下,发现了问题。。。我把锁加在循环里面了。。改下就好了。
结果如下:
image.png