Java多线程之生产者消费者问题:使用阻塞队列更优雅地解决生产者消费者问题

前一篇文章讲了如何使用java5中的重入锁和条件变量优雅地解决生产者消费者问题,本文将继续探究java并发包(concurrent),寻求更好的解决方案。

java并发包中提供了阻塞队列(BlockingQueue),查看该类的注释说明,大概翻译下:这是一个额外支持等待队列非空或等待队列可用的队列,意思就是当线程去队列取元素时,如果没有元素线程将等待,直到有可用的有元素可取;当向队列中添加元素时,判断队列是否已满,如果已满,则线程等待,直到队列可以添加元素。这样一看,阻塞队列完全可以解决生产者消费者问题。代码如下:

Consumer.java

package CreatorAndConsumerBlock;

public class Consumer implements Runnable {
    /**
     * 线程资源
     */
    private Plate plate;

    public Consumer(Plate plate) {
        this.plate = plate;
    }

    @Override
    public void run() {
        plate.getEgg();
    }
}
Creator.java
package CreatorAndConsumerBlock;

/**
 * 生产者
 *
 * @author Martin
 */
public class Creator implements Runnable {
    /**
     * 线程资源
     */
    private Plate plate;

    public Creator(Plate plate) {
        this.plate = plate;
    }

    @Override
    public void run() {
        Object egg = new Object();
        plate.addEgg(egg);
    }
}
Plate.java
package CreatorAndConsumerBlock;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 盘子,表示共享的资源
 *
 * @author Martin
 */
public class Plate {
    /**
     * 将arraylist换成阻塞队列,可见使用了阻塞队列后,代码中再也没有了lock、condition等了,完全不需要手动控制线程的等待还是唤醒了
     */
    private ArrayBlockingQueue<Object> eggs = new ArrayBlockingQueue<Object>(99999);

    /**
     * 获取蛋
     *
     * @return
     */
    public Object getEgg() {
        try {
            Object egg = eggs.take();
            System.out.println("消费者取蛋,当前剩余:" + eggs.size());
            return egg;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return null;
    }


    /**
     * 加入蛋
     *
     * @return
     */
    public void addEgg(Object egg) {
        try {
            eggs.put(new Object());
            System.out.println("生产者生蛋,当前剩余:" + eggs.size());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
Tester.java
package CreatorAndConsumerBlock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Tester {
    public static void main(String[] args)
    {
        //共享资源
        Plate plate = new Plate();
        ExecutorService pool = Executors.newFixedThreadPool(100);

        //添加生产者和消费者
        for(int i = 0 ; i < 100000; i ++)
        {
            pool.execute(new Creator(plate));
            pool.execute(new Consumer(plate));
        }

        pool.shutdown();
    }
}
和使用synchronized关键字和重入锁、条件变量相比,代码大大简化,程序中完全不需要关系线程如何同步资源,什么时候唤醒其他线程。其实,我们查看阻塞队列的源码,其实其也是使用重入锁、条件变量来实现的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章