Java并发编程 之实现生产者消费者


上联:线程、操作(对外暴露的调用方法)、资源类
下联:判断、干活、唤醒通知
横幅:严防多线程并发状态下的虚假唤醒

使用synchronize以及wait()、notify() /notifyAll()

class ShareData {

    public static AtomicInteger atomicInteger = new AtomicInteger();
    public volatile boolean flag = true;
    public static final int MAX_COUNT = 10;
    public static final List<Integer> pool = new ArrayList<>();

    public void produce() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            synchronized (pool) {
                // 判断
                if (pool.size() == MAX_COUNT) {
                    // 池子满了,生产者停止生产
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is full, waiting...");
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                pool.add(atomicInteger.incrementAndGet());
                System.out.println(Thread.currentThread().getName() + "\t produce number:" + atomicInteger.get() + "\t" + "current size:" + pool.size());
                //通知
                pool.notifyAll();
            }
        }
    }

    public void consumer() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            synchronized (pool) {
                // 判断
                if (pool.size() == 0) {
                    // 池子空的,停止消费
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is empty, waiting...");
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                System.out.println(Thread.currentThread().getName() + "\t consumer number:" + pool.get(0) + "\t" + "current size:" + pool.size());
                pool.remove(0);
                //通知
                pool.notifyAll();
            }

        }
    }

    public void stop() {
        flag = false;
    }

}

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            shareData.produce();
        }, "AAA").start();

        new Thread(() -> {
            shareData.consumer();
        }, "BBB").start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shareData.stop();
    }
}

上面的程序在只有两个线程时(一个生产者,一个消费者)可以正常工作。
在这里插入图片描述
但是把生产者和消费者线程扩展至多个。就出错了。例如再增加CCC和DDD线程分别生产和消费。只改动了main方法:

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            shareData.produce();
        }, "AAA").start();

        new Thread(() -> {
            shareData.consumer();
        }, "BBB").start();
        new Thread(() -> {
            shareData.produce();
        }, "CCC").start();

        new Thread(() -> {
            shareData.consumer();
        }, "DDD").start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shareData.stop();
    }
}

在这里插入图片描述
看到 current size 能到11了。这肯定出错了。因为要求pool的最大容量为10。出现这个情况的原因是在多线程的环境下,要防止虚假唤醒。即判断条件不能用 if,而是用 while。最终版正确的代码如下:

class ShareData {

    public static AtomicInteger atomicInteger = new AtomicInteger();
    public volatile boolean flag = true;
    public static final int MAX_COUNT = 10;
    public static final List<Integer> pool = new ArrayList<>();

    public void produce() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            synchronized (pool) {
                // 判断
                while (pool.size() == MAX_COUNT) {
                    // 池子满了,生产者停止生产
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is full, waiting...");
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                pool.add(atomicInteger.incrementAndGet());
                System.out.println(Thread.currentThread().getName() + "\t produce number:" + atomicInteger.get() + "\t" + "current size:" + pool.size());
                //通知
                pool.notifyAll();
            }
        }
    }

    public void consumer() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            synchronized (pool) {
                // 判断
                while (pool.size() == 0) {
                    // 池子空的,停止消费
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is empty, waiting...");
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                System.out.println(Thread.currentThread().getName() + "\t consumer number:" + pool.get(0) + "\t" + "current size:" + pool.size());
                pool.remove(0);
                //通知
                pool.notifyAll();
            }

        }
    }

    public void stop() {
        flag = false;
    }

}

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            shareData.produce();
        }, "AAA").start();

        new Thread(() -> {
            shareData.consumer();
        }, "BBB").start();
        new Thread(() -> {
            shareData.produce();
        }, "CCC").start();

        new Thread(() -> {
            shareData.consumer();
        }, "DDD").start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shareData.stop();
    }
}

在这里插入图片描述

使用Lock并结合Condition的await和signal方法

JUC包下的锁Lock替代synchronize关键字。await方法代替waitsignalAll代替notifyall

class ShareData {

    private static AtomicInteger atomicInteger = new AtomicInteger();
    private volatile boolean flag = true;
    private static final int MAX_COUNT = 10;
    private static final List<Integer> pool = new ArrayList<>();
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void produce() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            lock.lock();
            try {
                // 判断
                while (pool.size() == MAX_COUNT) {
                    // 池子满了,生产者停止生产
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is full, waiting...");
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                pool.add(atomicInteger.incrementAndGet());
                System.out.println(Thread.currentThread().getName() + "\t produce number:" + atomicInteger.get() + "\t" + "current size:" + pool.size());
                //通知
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    public void consumer() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            lock.lock();
            try {
                // 判断
                while (pool.size() == 0) {
                    // 池子空的,停止消费
                    try {
                        System.out.println(Thread.currentThread().getName() + "\t pool is empty, waiting...");
                        condition.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 干活
                System.out.println(Thread.currentThread().getName() + "\t consumer number:" + pool.get(0) + "\t" + "current size:" + pool.size());
                pool.remove(0);
                //通知
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    public void stop() {
        flag = false;
    }

}

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            shareData.produce();
        }, "AAA").start();

        new Thread(() -> {
            shareData.consumer();
        }, "BBB").start();
        new Thread(() -> {
            shareData.produce();
        }, "CCC").start();

        new Thread(() -> {
            shareData.consumer();
        }, "DDD").start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shareData.stop();
    }
}

阻塞队列版本

使用阻塞队列根本不需要去加锁,通知,完全通过阻塞队列进行阻塞和通知了。

class ShareData {

    private static AtomicInteger atomicInteger = new AtomicInteger();
    private volatile boolean flag = true;
    // 阻塞队列
    private static BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(10);

    public void produce() {
        while (flag) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
            }
            try {
                int incrementAndGet = atomicInteger.incrementAndGet();
                boolean value = blockingQueue.offer(incrementAndGet, 2, TimeUnit.SECONDS);
                int size = blockingQueue.size();
                if (value == true) {
                    System.out.println(Thread.currentThread().getName() + "\t 成功插入队列 \t" + incrementAndGet + "\t资源队列大小= " + size);
                } else {
                    System.out.println(Thread.currentThread().getName() + "\t 失败插入队列" + incrementAndGet + "\t资源队列大小= " + size);
                }
            } catch (InterruptedException e) {
            }
        }
    }

    public void consumer() {
        // 判断、干活、通知
        while (flag) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
            try {
                Integer result = blockingQueue.poll(2, TimeUnit.SECONDS);
                if (null == result) {
                    System.out.println("超过两秒没有取道数据,消费者即将退出");
                    return;
                }
                System.out.println(Thread.currentThread().getName() + "\t 成功消费 \t" + result + "\t" + "资源队列大小= " + blockingQueue.size());
            } catch (InterruptedException e) {
            }
        }
    }

    public void stop() {
        flag = false;
    }

}

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        ShareData shareData = new ShareData();
        new Thread(() -> {
            shareData.produce();
        }, "AAA").start();

        new Thread(() -> {
            shareData.consumer();
        }, "BBB").start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        shareData.stop();
    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值