并发编程栏目代码 GitHub package 地址: 点击打开链接
博客并发编程栏目 : 点击打开链接
在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列。
Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQueue,非阻塞队列的典型例子是ConcurrentLinkedQueue,在实际应用中要根据实际需要选用阻塞队列或者非阻塞队列。
注:什么叫线程安全?这个首先要明确。线程安全就是说多线程访问同一代码,不会产生不确定的结果。
详细方法使用参考:LinkedBlockingQueue 实现生产者消费者模型
对比锁机制的实现,使用无锁机制的难点在于要充分考虑线程间的协调。简单的说就是多个线程对内部数据结构进行访问时,如果其中一个线程执行的中途因为一些原因出现故障,其他的线程能够检测并帮助完成剩下的操作。这就需要把对数据结构的操作过程精细的划分成多个状态或阶段,考虑每个阶段或状态多线程访问会出现的情况。
ConcurrentLinkedQueue有两个volatile的线程共享变量:head,tail。要保证这个队列的线程安全就是保证对这两个Node的引用的访问(更新,查看)的原子性和可见性,由于volatile本身能够保证可见性,所以就是对其修改的原子性要被保证。
另外还说一下,ConcurrentLinkedQueue的size()是要遍历一遍集合的,所以尽量要避免用size而改用isEmpty(),以免性能过慢。
/* * 方法摘要 * boolean add(E e) * 将指定元素插入此队列的尾部。 * * boolean contains(Object o) * 如果此队列包含指定元素,则返回 true。 * * boolean isEmpty() * 如果此队列不包含任何元素,则返回 true。 * * Iterator<E> iterator() * 返回在此队列元素上以恰当顺序进行迭代的迭代器。 * * boolean offer(E e) * 将指定元素插入此队列的尾部。 * * E peek() * 获取但不移除此队列的头;如果此队列为空,则返回 null。 * * E poll() * 获取并移除此队列的头,如果此队列为空,则返回 null。 * * boolean remove(Object o) * 从队列中移除指定元素的单个实例(如果存在)。 * * int size() * 返回此队列中的元素数量。 * * Object[] toArray() * 返回以恰当顺序包含此队列所有元素的数组。 * * <T> T[] * toArray(T[] a) * 返回以恰当顺序包含此队列所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 */
阻塞算法其实很好理解,简单点理解就是加锁,比如在BlockingQueue中看到的那样,再往前推点,那就是synchronized。相比而言,非阻塞算法的设计和实现都很困难,要通过低级的原子性来支持并发。下面就简要的介绍一下非阻塞算法,以下部分的内容参照了一篇很经典的文章http://www.ibm.com/developerworks/cn/java/j-jtp04186/
注:我觉得可以这样理解,阻塞对应同步,非阻塞对应并发。也可以说:同步是阻塞模式,异步是非阻塞模式 ??吗??
其实,最终了解的应该是阻塞和非阻塞的区别,运用阻塞队列还是非阻塞队列去完成需求,以及两者的底层实现方式。
本文对队列的入门以及使用时候注意事项进行测试说明。
对于阻塞等待与轮询判断,区别到底在哪儿,性能上呢。
研究以后上链接。
/** * @author wei.Li by 14-8-28. */ public class QueueCompare { private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(QueueCompare.class); /** * 生产者、消费者 */ interface Market<V> { void producer(V v); void consumer(); } /** * concurrentLinkedQueue 的生产与消费实现 */ private static class ConcurrentLinkedQueueMarket<V> implements Market<V> { @Override public void producer(V o) { concurrentLinkedQueue.add(o); // LOGGER.info("concurrentLinkedQueue <{}> producer <{}>", concurrentLinkedQueue, o); } @Override public void consumer() { while (!concurrentLinkedQueue.isEmpty()) {//return first() == null; !!! size 方法是遍历队列返回总数 concurrentLinkedQueue.poll(); // LOGGER.info("concurrentLinkedQueue <{}> consumer <{}>", linkedBlockingQueue, o); } } } /** * linkedBlockingQueue 的生产与消费实现 */ private static class LinkedBlockingQueueMarket<V> implements Market<V> { @Override public void producer(V o) { try { linkedBlockingQueue.put(o); //LOGGER.info("linkedBlockingQueue <{}> producer <{}>", linkedBlockingQueue, o); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void consumer() { while (!linkedBlockingQueue.isEmpty()) {//return size() == 0; 与直接使用 size 方法无区别 try { linkedBlockingQueue.take(); // LOGGER.info("linkedBlockingQueue <{}> consumer <{}>", linkedBlockingQueue, o); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 生产处理线程 * * @param <T> extends Market */ private static class ProducerHandle<T extends Market<V>, V> implements Runnable { T market; V v; private ProducerHandle(T market, V v) { this.market = market; this.v = v; } @Override public void run() { for (int i = 0; i < PRODUCER_OBJ_NUM; i++) { market.producer(v); } } } /** * 消费处理线程 * * @param <T> extends Market */ private static class ConsumerHandle<T extends Market<V>, V> implements Runnable { T market; V v; private ConsumerHandle(T market, V v) { this.market = market; this.v = v; } @Override public void run() { market.consumer(); LOGGER.info(" <{}> done <{}> need time <{}>" , market.getClass().getSimpleName() , PRODUCER_OBJ_NUM , DateTime.now().toString(ISODateTimeFormat.dateHourMinuteSecond())); } } //执行的线程数量 private static final int SYNCHRONIZED_DONE_THREAD_NUM = 4; //线程池 private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(SYNCHRONIZED_DONE_THREAD_NUM); //linkedBlockingQueue init private static LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(); //concurrentLinkedQueue init private static ConcurrentLinkedQueue concurrentLinkedQueue = new ConcurrentLinkedQueue(); //测试生产数量 public static final int PRODUCER_OBJ_NUM = 10000000; private static void runTest() { /** * 添加concurrentLinkedQueue生产线程 */ Market<String> concurrentLinkedQueueMarket = new ConcurrentLinkedQueueMarket<>(); EXECUTOR_SERVICE.execute( new ProducerHandle<>(concurrentLinkedQueueMarket, "concurrentLinkedQueueMarket") ); EXECUTOR_SERVICE.execute( new ConsumerHandle<>(concurrentLinkedQueueMarket, "concurrentLinkedQueueMarket") ); /** * 添加blockingQueue生产线程 */ Market<String> blockingQueueMarket = new LinkedBlockingQueueMarket<>(); EXECUTOR_SERVICE.execute( new ProducerHandle<>(blockingQueueMarket, "blockingQueueMarket") ); EXECUTOR_SERVICE.execute( new ConsumerHandle<>(blockingQueueMarket, "blockingQueueMarket") ); EXECUTOR_SERVICE.shutdown(); } public static void main(String[] args) { runTest(); } }
ConcurrentLinkedQueue的size()是要遍历一遍集合的,所以尽量要避免用size而改用isEmpty(),以免性能过慢。