publicclassSingletonConsumer{ privatefinalstatic Logger logger = Logger.getLogger(SingletonConsumer.class); privatestatic SingletonConsumer instance; privateSingletonConsumer(){ } public SingletonConsumer getInstance(){ if (instance == null) { logger.debug("instance is null, trying to instantiate a new one"); instance = new SingletonConsumer(); } else { logger.debug("instance is not null, return the already-instantiated one"); } return instance; } }
@Test publicvoidsingletonConsumerTest()throws InterruptedException { ExecutorService executors = Executors.newFixedThreadPool(10); Set<SingletonConsumer> set = new HashSet<>(); for(int i = 0; i < 10; i++){ executors.execute( () -> set.add(SingletonConsumer.getInstance()) ); } executors.shutdown(); executors.awaitTermination(1, TimeUnit.HOURS); Assert.assertEquals(10, set.size()); }
运行测试,输出结果如下
1 2 3 4 5 6 7 8 9 10 11
2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one 2017-02-26 13:50:52 DEBUG SingletonConsumer:20 - instance is null, trying to instantiate a new one set size:10
publicstatic SingletonConsumer getInstance(){ synchronized (SingletonConsumer.class) { if (instance == null) { logger.debug("instance is null, trying to instantiate a new one"); instance = new SingletonConsumer(); } else { logger.debug("instance is not null, return the already-instantiated one"); } return instance; } }
我们再次运行测试脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14
2017-02-26 14:01:12 DEBUG SingletonConsumer:21 - instance is null, trying to instantiate a new one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one 2017-02-26 14:01:12 DEBUG SingletonConsumer:24 - instance is not null, return the already-instantiated one java.lang.AssertionError: Expected :10 Actual :1 <Click to see difference>
if (instance == null) { synchronized (SingletonConsumer.class) { logger.debug("instance is null, trying to instantiate a new one"); instance = new SingletonConsumer(); } } else { logger.debug("instance is not null, return the already-instantiated one"); } return instance;
2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one 2017-02-26 14:14:18 DEBUG SingletonConsumer:28 - instance is null, trying to instantiate a new one set size:10
publicstatic SingletonConsumer getInstance(){ if (instance == null) { synchronized (SingletonConsumer.class) { if (instance == null) { logger.debug("instance is null, trying to instantiate a new one"); instance = new SingletonConsumer(); } else { logger.debug("instance is not null, return the already-instantiated one "); } } } else { logger.debug("instance is not null, return the already-instantiated one"); } return instance; }
public String processLine(String line)throws IOException { int min = 1; int max = 100; int randomMillisecconds = min + (int)(Math.random() * ((max - min) + 1)); try { Thread.sleep(randomMillisecconds); } catch (InterruptedException e) { e.printStackTrace(); } // readLineCount is not accurate in multi-thread mode readLineCount++; logger.info(String.format("%d lines were read.", readLineCount)); return line; }
2017-03-01 13:52:04 INFO FileUtil:118 - 2954 lines were read. 2017-03-01 13:52:04 INFO FileUtil:118 - 2955 lines were read. 2017-03-01 13:52:04 INFO FileUtil:118 - 2956 lines were read. 2017-03-01 13:52:04 INFO FileUtil:118 - 2957 lines were read. 2017-03-01 13:52:04 INFO FileUtil:118 - 2958 lines were read. 2017-03-01 13:52:04 DEBUG Starter:50 - Consumer's totalCount: 3000 2017-03-01 13:52:04 INFO Starter:53 - It takes 16 seconds to finish
解释一下,诸如2958 lines were read,是从FileUtil工具类processLine函数中打印出的readLine变量,其代表意义是线程Consumer线程从queue中读取了多少行。Consumer’s totalCount: 3000 是直接打印的Consumer全局变totalCount,其代表意义同样是十个线程总共从queue中读取了多少行,按道理来说,这两个值是应该相同的,然后结果明显不一致。
public String processLine(String line)throws IOException { synchronized (this) { int min = 1; int max = 100; int randomMillisecconds = min + (int) (Math.random() * ((max - min) + 1)); try { Thread.sleep(randomMillisecconds); } catch (InterruptedException e) { e.printStackTrace(); } // readLineCount is not accurate in multi-thread mode readLineCount++; logger.info(String.format("%d lines were read.", readLineCount)); return line; } }
我们再次运行测试脚本:
1 2 3 4 5 6 7 8 9
2017-03-01 14:09:34 INFO FileUtil:119 - 2994 lines were read. 2017-03-01 14:09:34 INFO FileUtil:119 - 2995 lines were read. 2017-03-01 14:09:34 INFO FileUtil:119 - 2996 lines were read. 2017-03-01 14:09:34 INFO FileUtil:119 - 2997 lines were read. 2017-03-01 14:09:35 INFO FileUtil:119 - 2998 lines were read. 2017-03-01 14:09:35 INFO FileUtil:119 - 2999 lines were read. 2017-03-01 14:09:35 INFO FileUtil:119 - 3000 lines were read. 2017-03-01 14:09:35 DEBUG Starter:50 - Consumer's totalCount: 3000 2017-03-01 14:09:35 INFO Starter:53 - It takes 160 seconds to finish
private AtomicInteger readLineCount = new AtomicInteger(0);
processLine函数变为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public String processLine(String line)throws IOException { int min = 1; int max = 100; int randomMillisecconds = min + (int) (Math.random() * ((max - min) + 1)); try { Thread.sleep(randomMillisecconds); } catch (InterruptedException e) { e.printStackTrace(); } // readLineCount is not accurate in multi-thread mode readLineCount.incrementAndGet(); logger.info(String.format("%d lines were read.", readLineCount.get())); return line; }
再次运行测试代码:
1 2 3 4 5 6 7 8 9
2017-03-01 14:18:23 INFO FileUtil:120 - 2994 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 2995 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 2996 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 2997 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 2998 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 2999 lines were read. 2017-03-01 14:18:23 INFO FileUtil:120 - 3000 lines were read. 2017-03-01 14:18:23 DEBUG Starter:50 - Consumer's totalCount: 3000 2017-03-01 14:18:23 INFO Starter:53 - It takes 15 seconds to finish