A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© wnmmp 中级黑马   /  2014-8-10 00:24  /  1959 人查看  /  9 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

《张孝祥Java多线程与并发库高级应用视频教程》中关于“空中网面试题1”的代码,用了BlockingQueue阻塞队列,增加了一个循环,为什么不用Executors?改进完以后不用增加循环,而且代码更简单,结果一样。
这是原文代码:
  1. package read;

  2. import java.util.concurrent.ArrayBlockingQueue;
  3. import java.util.concurrent.BlockingQueue;

  4. public class Test {
  5.        
  6.         public static void main(String[] args){
  7.         final BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1);
  8.                 for(int i=0;i<4;i++){
  9.                         new Thread(new Runnable(){
  10.                                 @Override
  11.                                 public void run() {
  12.                                         while(true){
  13.                                                 try {
  14.                                                         String log = queue.take();
  15.                                                         parseLog(log);
  16.                                                 } catch (InterruptedException e) {
  17.                                                         // TODO Auto-generated catch block
  18.                                                         e.printStackTrace();
  19.                                                 }
  20.                                         }
  21.                                 }
  22.                                
  23.                         }).start();
  24.                 }
  25.                
  26.                 System.out.println("begin:"+(System.currentTimeMillis()/1000));
  27.                 /*模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
  28.                 修改程序代码,开四个线程让这16个对象在4秒钟打完。
  29.                 */
  30.                 for(int i=0;i<16;i++){  //这行代码不能改动
  31.                         final String log = ""+(i+1);//这行代码不能改动
  32.                         {
  33.                                         try {
  34.                                                 queue.put(log);
  35.                                         } catch (InterruptedException e) {
  36.                                                 // TODO Auto-generated catch block
  37.                                                 e.printStackTrace();
  38.                                         }
  39.                                      //Test.parseLog(log);
  40.                         }
  41.                 }
  42.         }
  43.        
  44.         //parseLog方法内部的代码不能改动
  45.         public static void parseLog(String log){
  46.                 System.out.println(log+":"+(System.currentTimeMillis()/1000));
  47.                
  48.                 try {
  49.                         Thread.sleep(1000);
  50.                 } catch (InterruptedException e) {
  51.                         e.printStackTrace();
  52.                 }               
  53.         }
  54.        
  55. }
复制代码

这是改进的:
  1. package read;

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

  4. public class t {

  5.         public static void main(String[] args) {

  6.                 System.out.println("begin:" + (System.currentTimeMillis() / 1000));
  7.                 /*
  8.                  * 模拟处理16行日志,下面的代码产生了16个日志对象,当前代码需要运行16秒才能打印完这些日志。
  9.                  * 修改程序代码,开四个线程让这16个对象在4秒钟打完。
  10.                  */
  11.                 ExecutorService es = Executors.newFixedThreadPool(4);
  12.                 for (int i = 0; i < 16; i++) { // 这行代码不能改动
  13.                         final String log = "" + (i + 1);// 这行代码不能改动
  14.                         es.execute(new Runnable() {
  15.                                 @Override
  16.                                 public void run() {
  17.                                         // TODO Auto-generated method stub
  18.                                         Test.parseLog(log);
  19.                                 }
  20.                         });
  21.                 }
  22.                 es.shutdown();
  23.         }

  24.         // parseLog方法内部的代码不能改动
  25.         public static void parseLog(String log) {
  26.                 System.out.println(log + ":" + (System.currentTimeMillis() / 1000));

  27.                 try {
  28.                         Thread.sleep(1000);
  29.                 } catch (InterruptedException e) {
  30.                         e.printStackTrace();
  31.                 }
  32.         }

  33. }
复制代码

9 个回复

倒序浏览
你这样肯定是不行的,你开的那4个线程没有指定顺序,完全可能输出2、1、4、3这种顺序。我拿你的代码运行就输出了:
begin:1407602421
1:1407602421
2:1407602421
3:1407602421
4:1407602421
5:1407602422
6:1407602422
7:1407602422
8:1407602422
9:1407602423
12:1407602423
11:1407602423
10:1407602423
13:1407602424
16:1407602424
15:1407602424
14:1407602424
很明显顺序乱了
回复 使用道具 举报
M单色调 来自手机 中级黑马 2014-8-10 01:30:52
藤椅
嗯。线程没有指定顺序!
回复 使用道具 举报
额。。题目中没有要求有序
回复 使用道具 举报
fantacyleo 发表于 2014-8-10 00:43
你这样肯定是不行的,你开的那4个线程没有指定顺序,完全可能输出2、1、4、3这种顺序。我拿你的代码运行就 ...

张老师的也没有指定顺序,你多运行几遍也会乱序,因为在第一秒内,四个线程依次生成,他们在take()的时候应该是有序的,但是在后面三秒等待take()的时候也是随意获取的,如果安排他们顺序的话,时间就要应该就不是4秒了。当然我的更乱。
这是某一次的运行结果(看13、14):
begin:1407686503
1:1407686503
2:1407686503
3:1407686503
4:1407686503
5:1407686504
6:1407686504
7:1407686504
8:1407686504
9:1407686505
10:1407686505
11:1407686505
12:1407686505
14:1407686506
13:1407686506
15:1407686506
16:1407686506
回复 使用道具 举报
wnmmp 发表于 2014-8-11 00:11
张老师的也没有指定顺序,你多运行几遍也会乱序,因为在第一秒内,四个线程依次生成,他们在take()的时候 ...

所以张老师的解法也是有问题的。。。因为原先的代码直接运行是有序的,而张老师之所以把BlockingQueue的size设为1,其意图也是想保持有序,但事与愿违。第三题张老师的解法也有问题,几年前有人给他发邮件指出,他也承认了
回复 使用道具 举报
wnmmp 中级黑马 2014-8-11 02:50:00
7#
fantacyleo 发表于 2014-8-11 00:19
所以张老师的解法也是有问题的。。。因为原先的代码直接运行是有序的,而张老师之所以把BlockingQueue的s ...

哇塞,你好牛!知道的很多啊
回复 使用道具 举报
fantacyleo 发表于 2014-8-11 00:19
所以张老师的解法也是有问题的。。。因为原先的代码直接运行是有序的,而张老师之所以把BlockingQueue的s ...

那么,应该怎么解比较好?
回复 使用道具 举报
a6511631 发表于 2014-8-11 08:23
那么,应该怎么解比较好?

在楼主解法的
  1. es.execute(new Runnable() {
  2.                                 @Override
  3.                                 public void run() {
  4.                                         // TODO Auto-generated method stub
  5.                                         Test.parseLog(log);
  6.                                 }
  7.                         });
复制代码

语句之后加一句Thread.sleep(10);  这是我目前唯一能想出来的办法。网上也有不少人尝试顺序输出,但都不行
回复 使用道具 举报
wnmmp 中级黑马 2014-8-12 23:14:46
10#
fantacyleo 发表于 2014-8-11 10:36
在楼主解法的

语句之后加一句Thread.sleep(10);  这是我目前唯一能想出来的办法。网上也有不少人尝试顺 ...

这个还算可行,要是能知道线程运行的时间就更好设置sleep时间了
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马