在Grizzly中,自带了LinkedTransferQueue,和JDK 7自带的LinkedTransferQueue有所不同,不同之处就是使用PaddedAtomicReference来提升并发性能,其实这是一种错误的编码技巧,没有意义!
AtomicReference和LinkedTransferQueue的本质是乐观锁,乐观锁的在激烈竞争的时候性能都很糟糕,乐观锁应使用在非激烈竞争的场景,为乐观锁优化激烈竞争下的性能,是错误的方向,因为如果需要激烈竞争,就应该使用悲观锁。
以下是一个JDK中内置乐观锁悲观锁的对照表:
乐观锁 -----> 悲观锁
AtomicInteger -----> Lock + volatile int
AtomicLong -----> Lock + volatile long
AtomicReference -----> Lock + volatile
LinkedTransferQueue -----> LinkedBlockingQueue
在激烈竞争中,LinkedTransferQueue的性能,远远低于LinkedBlockingQueue,使用PaddedAtomicReference优化也是一样的。如果不激烈竞争,Padded-LinkedTransferQueue和LinkedTransferQueue相比也没有什么优势。
所以Padded-AtomicReference也是一个伪命题,如果激励竞争,为什么不使用Lock + volatile,如果非激烈竞争,使用PaddedAtomicReference对于AtomicReference又没有优势。所以使用Padded-AtomicReference是一个错误的编码技巧。
以下是测试代码,50个线程争用10个对象,这种激烈竞争下,使用LinkedTransferQueue比LinkedBlockingQueue大约慢10倍。
1package com.alibaba.study; 2 3import java.util.concurrent.*; 4 5public class BlockingQueueTest { 6 public static void main(String[] args) throws Exception { 7 for (int i = 0; i < 3; ++i) { 8 loop(); 9 } 10 } 11 12 private static void loop() throws InterruptedException { 13 final BlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(); 14// final BlockingQueue<Object> queue = new LinkedTransferQueue<Object>(); 15 16 for (int i = 0; i < 10; ++i) { 17 queue.put(i); 18 } 19 20 final int THREAD_COUNT = 50; 21 final CountDownLatch startLatch = new CountDownLatch(1); 22 final CountDownLatch endLatch = new CountDownLatch(THREAD_COUNT); 23 24 for (int i = 0; i < THREAD_COUNT; ++i) { 25 Thread thread = new Thread() { 26 public void run() { 27 try { 28 startLatch.await(); 29 } catch (InterruptedException e) { e.printStackTrace(); } 30 31 try { 32 for (int i = 0; i < 1000 * 20; ++i) { 33 Object item = queue.take(); 34 queue.put(item); 35 } 36 } catch (Exception e) { 37 e.printStackTrace(); 38 } finally { 39 endLatch.countDown(); 40 } 41 } 42 }; 43 thread.start(); 44 } 45 46 long startMillis = System.currentTimeMillis(); 47 startLatch.countDown(); 48 endLatch.await(); 49 long millis = System.currentTimeMillis() - startMillis; 50 System.out.println(queue.getClass().getName() + " : " + millis); 51 } 52}