关闭

java.util.concurrent 编程范例

550人阅读 评论(0) 收藏 举报

http://kanglecjr.iteye.com/blog/1070934

java.util.concurrent 之一

Java代码 复制代码 收藏代码
  1. package test;   
  2.   
  3. import java.util.concurrent.ExecutorService;   
  4. import java.util.concurrent.Executors;   
  5. public class TestThreadPool {   
  6.     public static void main(String args[]) throws InterruptedException{   
  7.     // only two threads   
  8.     ExecutorService exec = Executors.newFixedThreadPool(2);   
  9.     for(int index=0; index < 10; index++){   
  10.         Runnable run = new Runnable(){   
  11.             public void run(){   
  12.                 long time = (long)(Math.random() * 1000);   
  13.                 System.out.println("Sleeping " + time + "ms");   
  14.                 try{   
  15.                     Thread.sleep(time);   
  16.                 }   catch(InterruptedException e){   
  17.                 }   
  18.             }   
  19.         };   
  20.         exec.execute(run);   
  21.     }   // must shutdown   
  22.     exec.shutdown();   
  23. }   
  24.     }  


可能的运行结果:
Java代码 复制代码 收藏代码
  1. Sleeping 543ms   
  2. Sleeping 33ms   
  3. Sleeping 658ms   
  4. Sleeping 740ms   
  5. Sleeping 145ms   
  6. Sleeping 216ms   
  7. Sleeping 890ms   
  8. Sleeping 864ms   
  9. Sleeping 786ms   
  10. Sleeping 644ms  


    上面是一个简单的例子。使用大小为2的线程池来处理10个线程。
它使用了Executors的静态函数 newFixedThreadPool() 生成一个固定的线程池。顾名思义,
线程池是不会释放的,即使它是Idle。这就会产生性能问题。比如如果线程池的大小为200,当全部使用完毕后,
所有的线程会继续留在池中,相应的内存和线程切换(while(true)+sleep)都会增加。如果要避免这个问题,就
必须直接使用ThreadPoolExecutor()来构造。可以像Tomcat的线程池一样,设置“最大线程数”、“最小线程数”
和“空闲线程keepAlive的时间”。通过这些可以基本上替换Tomcat的线程池实现方案。
需要注意的是线程池必须使用shutdown来显式关闭,否则主线程就无法退出。

 

java.util.concurrent 范例之二:周期性运行

http://kanglecjr.iteye.com/blog/1071022

Java代码 复制代码 收藏代码
  1. package test;   
  2.   
  3. import static java.util.concurrent.TimeUnit.SECONDS;   
  4. import java.util.Date;   
  5. import java.util.concurrent.Executors;   
  6. import java.util.concurrent.ScheduledExecutorService;   
  7. import java.util.concurrent.ScheduledFuture;   
  8. public class TestScheduledThread {   
  9. /**  
  10.  * @param args  
  11.  */  
  12. public static void main(String[] args) {   
  13.    final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);   
  14.    final Runnable beeper = new Runnable() {   
  15.     int count = 0;   
  16.     public void run() {   
  17.      System.out.println(new Date() + " beep " + (++count));   
  18.     }   
  19.    };   
  20.    // 1秒钟后运行,并每隔2秒运行一次   
  21.    final ScheduledFuture beeperHandle = scheduler.scheduleAtFixedRate(beeper, 12, SECONDS);   
  22.    // 2秒钟后运行,并每次在上次任务运行完后等待5秒后重新运行   
  23.    final ScheduledFuture beeperHandle2 = scheduler.scheduleWithFixedDelay(beeper, 25, SECONDS);   
  24.    // 30秒后结束关闭任务,并且关闭Scheduler   
  25.    scheduler.schedule(new Runnable() {   
  26.     public void run() {   
  27.      beeperHandle.cancel(true);   
  28.      beeperHandle2.cancel(true);   
  29.      scheduler.shutdown();   
  30.     }   
  31.    }, 30, SECONDS);   
  32. }   
  33. }  



可能的运行结果:

Java代码 复制代码 收藏代码
  1. Mon Jun 06 21:26:52 CST 2011 beep 1  
  2. Mon Jun 06 21:26:53 CST 2011 beep 2  
  3. Mon Jun 06 21:26:54 CST 2011 beep 3  
  4. Mon Jun 06 21:26:56 CST 2011 beep 4  
  5. Mon Jun 06 21:26:58 CST 2011 beep 5  
  6. Mon Jun 06 21:26:58 CST 2011 beep 6  
  7. Mon Jun 06 21:27:00 CST 2011 beep 7  
  8. Mon Jun 06 21:27:02 CST 2011 beep 8  
  9. Mon Jun 06 21:27:03 CST 2011 beep 9  
  10. Mon Jun 06 21:27:04 CST 2011 beep 10  
  11. Mon Jun 06 21:27:06 CST 2011 beep 11  
  12. Mon Jun 06 21:27:08 CST 2011 beep 12  
  13. Mon Jun 06 21:27:08 CST 2011 beep 13  
  14. Mon Jun 06 21:27:10 CST 2011 beep 14  
  15. Mon Jun 06 21:27:12 CST 2011 beep 15  
  16. Mon Jun 06 21:27:13 CST 2011 beep 16  
  17. Mon Jun 06 21:27:14 CST 2011 beep 17  
  18. Mon Jun 06 21:27:16 CST 2011 beep 18  
  19. Mon Jun 06 21:27:18 CST 2011 beep 19  
  20. Mon Jun 06 21:27:18 CST 2011 beep 20  
  21. Mon Jun 06 21:27:20 CST 2011 beep 21  



许多长时间运行的应用有时候需要定时运行任务完成一些诸如统计、优化等工作,比如在电信行业中处理用户话单时,需要每隔1分钟处理话单;网站每天凌晨统计用户访问量、用户数;大型超市凌晨3点统计当天销售额、以及最热卖的商品;每周日进行数据库备份;公司每个月的10号计算工资并进行转帐等,这些都是定时任务。通过 java 的并发库concurrent可以轻松的完成这些任务,而且非常的简单。

为了退出进程,上面的代码中加入了关闭Scheduler的操作。而对于24小时运行的应用而言,是没有必要关闭Scheduler的。

http://kanglecjr.iteye.com/blog/1071042

    在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等待其他线程都完成某一阶段后再执行,等所有线程都到达某一个阶段后再统一执行。

    比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉。

  这时候 CyclicBarrier 就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。
Java代码 复制代码 收藏代码
  1. package test;   
  2.   
  3. import java.text.SimpleDateFormat;   
  4. import java.util.Date;   
  5. import java.util.concurrent.BrokenBarrierException;   
  6. import java.util.concurrent.CyclicBarrier;   
  7. import java.util.concurrent.ExecutorService;   
  8. import java.util.concurrent.Executors;   
  9. public class TestCyclicBarrier{   
  10.     // 徒步需要的時間   
  11.     private static int[] timeWalk = { 58151510 };   
  12.     // 自駕遊   
  13.     private static int[] timeSelf = { 13445 };   
  14.     // 旅遊大巴   
  15.     private static int[] timeBus = { 2,4,6,6,7 };   
  16.        
  17.     static String now(){   
  18.         SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");   
  19.         return sdf.format(new Date()) + ": ";   
  20.     }   
  21.     static class Tour implements Runnable{   
  22.         private int[] times;   
  23.         private CyclicBarrier barrier;   
  24.         private String tourName;   
  25.         public Tour(CyclicBarrier barrier, String tourName, int[] times){   
  26.             this.times=times;   
  27.             this.tourName=tourName;   
  28.             this.barrier=barrier;   
  29.         }   
  30.         public void run(){   
  31.             try{   
  32.                 Thread.sleep(times[0]*1000);   
  33.                 System.out.println(now() + tourName + " Reached Shenzhen");   
  34.                 barrier.await();   
  35.                 Thread.sleep(times[1]*1000);   
  36.                 System.out.println(now() + tourName + " Reached Guangzhou");   
  37.                 barrier.await();   
  38.                 Thread.sleep(times[2]*1000);   
  39.                 System.out.println(now() + tourName + " Reached Shaoguan");   
  40.                 barrier.await();   
  41.                 Thread.sleep(times[3]*1000);   
  42.                 System.out.println(now() + tourName + " Reached Changsha");   
  43.                 barrier.await();   
  44.                 Thread.sleep(times[4]*1000);   
  45.                 System.out.println(now() + tourName + " Reached Wuhan");   
  46.                 barrier.await();   
  47.         }catch(InterruptedException e){   
  48.         }catch(BrokenBarrierException e){   
  49.         }   
  50.     }   
  51. }   
  52. public static void main(String[] args){   
  53.     // 三個旅行團   
  54.     CyclicBarrier barrier = new CyclicBarrier(3);   
  55.     ExecutorService exec = Executors.newFixedThreadPool(3);   
  56.     exec.submit(new Tour(barrier, "WalkTour", timeWalk));   
  57.     exec.submit(new Tour(barrier, "SelfTour", timeSelf));   
  58.     //   
  59.     exec.submit(new Tour(barrier, "BusTour", timeBus));   
  60.     exec.shutdown();   
  61.   }   
  62. }  


可能的执行结果:
Java代码 复制代码 收藏代码
  1. 21:47:18: SelfTour Reached Shenzhen   
  2. 21:47:19: BusTour Reached Shenzhen   
  3. 21:47:22: WalkTour Reached Shenzhen   
  4. 21:47:25: SelfTour Reached Guangzhou   
  5. 21:47:26: BusTour Reached Guangzhou   
  6. 21:47:30: WalkTour Reached Guangzhou   
  7. 21:47:34: SelfTour Reached Shaoguan   
  8. 21:47:36: BusTour Reached Shaoguan   
  9. 21:47:45: WalkTour Reached Shaoguan   
  10. 21:47:49: SelfTour Reached Changsha   
  11. 21:47:51: BusTour Reached Changsha   
  12. 21:48:00: WalkTour Reached Changsha   
  13. 21:48:05: SelfTour Reached Wuhan   
  14. 21:48:07: BusTour Reached Wuhan   
  15. 21:48:10: WalkTour Reached Wuhan  

http://kanglecjr.iteye.com/blog/1071069

    并发库中的BlockingQueue是一个比较好玩的类,顾名思义,就是阻塞队列。该类主要提供了两个方法put()和take(),前者将一个对象放到队列尾部,如果队列已经满了,就等待直到有空闲节点;后者从head取一个对象,如果没有对象,就等待直到有可取的对象。
Java代码 复制代码 收藏代码
  1. package test;   
  2.   
  3. import java.util.concurrent.BlockingQueue;   
  4. import java.util.concurrent.ExecutorService;   
  5. import java.util.concurrent.Executors;   
  6. import java.util.concurrent.LinkedBlockingQueue;   
  7.   
  8. public class MyBlockingQueue extends Thread{   
  9.     public static BlockingQueue<String> queue=new LinkedBlockingQueue<String>(3);   
  10.     private int index;   
  11.     public MyBlockingQueue(int i){   
  12.         this.index=i;   
  13.     }   
  14.   
  15.     public void run(){   
  16.         try{   
  17.             queue.put(String.valueOf(this.index));   
  18.             System.out.println("put {"+this.index+"} into queue!");   
  19.         }catch(Exception e){   
  20.             e.printStackTrace();   
  21.         }   
  22.     }   
  23.        
  24.     public static void main(String args[]){   
  25.         ExecutorService service=Executors.newCachedThreadPool();   
  26.         forint i=0; i<10; i++){   
  27.             service.submit(new MyBlockingQueue(i));   
  28.         }   
  29.         Thread thread = new Thread(){   
  30.             public void run(){   
  31.                 try{   
  32.                     while(true){   
  33.                         Thread.sleep((int)(Math.random()*1000));   
  34.                         if(MyBlockingQueue.queue.isEmpty()) break;   
  35.                         String str=MyBlockingQueue.queue.take();   
  36.                         System.out.println("take {" + str+"} out of queue!");   
  37.                     }   
  38.                 }catch(Exception e){   
  39.                     e.printStackTrace();   
  40.                 }   
  41.             }   
  42.         };   
  43.         service.submit(thread);   
  44.         service.shutdown();   
  45.     }   
  46.        
  47. }         

可能的运行结果:
Java代码 复制代码 收藏代码
  1. put {0} into queue!   
  2. put {1} into queue!   
  3. put {3} into queue!   
  4. take {0} out of queue!   
  5. put {2} into queue!   
  6. take {1} out of queue!   
  7. put {7} into queue!   
  8. take {3} out of queue!   
  9. put {5} into queue!   
  10. take {2} out of queue!   
  11. put {4} into queue!   
  12. take {7} out of queue!   
  13. put {6} into queue!   
  14. put {9} into queue!   
  15. take {5} out of queue!   
  16. take {4} out of queue!   
  17. put {8} into queue!   
  18. take {6} out of queue!   
  19. take {9} out of queue!   
  20. take {8} out of queue!  

http://kanglecjr.iteye.com/blog/1071107

    从名字可以看出,CountDownLatch是一个倒数计数的锁,当倒数到0时触发事件,也就是开锁,其他人就可以进入了。在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;同时当线程都完成后也会触发事件,以便进行后面的操作。


CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。

一个CountDownLatch实例是不能重复使用的,也就是说它是一次性的,锁一经被打开就不能再关闭使用了,如果想重复使用,请考虑使用CyclicBarrier。

下面的例子简单的说明了CountDownLatch的使用方法,模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。

同样,线程池需要显式shutdown。
Java代码 复制代码 收藏代码
  1. package test;   
  2.   
  3. import java.util.concurrent.CountDownLatch;   
  4. import java.util.concurrent.ExecutorService;   
  5. import java.util.concurrent.Executors;   
  6.   
  7. public class TestCountDownLatch{   
  8.     public static void main(String[] args) throws InterruptedException{   
  9.         //开始的倒数锁   
  10.         final CountDownLatch begin=new CountDownLatch(1);   
  11.         //结束的倒数锁   
  12.         final CountDownLatch end=new CountDownLatch(10);   
  13.         //10名选手   
  14.         final ExecutorService exec=Executors.newFixedThreadPool(10);   
  15.            
  16.         for(int index=0; index<10;index++){   
  17.             final int NO=index + 1;//Cannot refer to a non-final variable NO inside an inner class defined in a different method   
  18.             Runnable run=new Runnable(){   
  19.                 public void run(){   
  20.                     try{   
  21.                         begin.await();//一直阻塞   
  22.                         Thread.sleep((long)(Math.random() * 10000));   
  23.                         System.out.println("No." + NO + " arrived");   
  24.                     }catch(InterruptedException e){   
  25.                     }finally{   
  26.                         end.countDown();   
  27.                     }   
  28.                 }   
  29.             };   
  30.             exec.submit(run);   
  31.         }   
  32.         System.out.println("Game Start");   
  33.         begin.countDown();   
  34.         end.await();   
  35.         System.out.println("Game Over");   
  36.         exec.shutdown();   
  37.     }   
  38. }  


可能的结果:
Java代码 复制代码 收藏代码
  1. Game Start   
  2. No.4 arrived   
  3. No.6 arrived   
  4. No.8 arrived   
  5. No.5 arrived   
  6. No.10 arrived   
  7. No.3 arrived   
  8. No.2 arrived   
  9. No.7 arrived   
  10. No.1 arrived   
  11. No.9 arrived   
  12. Game Over  

 

    有时候在实际应用中,某些操作很耗时,但又不是不可或缺的步骤。比如用网页浏览器浏览新闻时,最重要的是显示文字内容,至于与新闻相匹配的图片就没有那么重要的,所以此时首先保证文字信息先显示,而图片信息会后显示,但又不能不显示,由于下载图片是一个耗时的操作,所以必须一开始就得下载。


Java的并发库的Future类就可以满足这个要求。Future的重要方法包括get()和cancel(),get()获取数据对象,如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。

下面的Demo简单的说明了Future的使用方法:一个非常耗时的操作必须一开始启动,但又不能一直等待;其他重要的事情又必须做,等完成后,就可以做不重要的事情。
Java代码 复制代码 收藏代码
  1. package concurrent;   
  2.   
  3. import java.util.concurrent.Callable;   
  4. import java.util.concurrent.ExecutionException;   
  5. import java.util.concurrent.ExecutorService;   
  6. import java.util.concurrent.Executors;   
  7. import java.util.concurrent.Future;   
  8.   
  9. public class TestFutureTask {   
  10.     public static void main(String[] args) throws InterruptedException,ExecutionException{   
  11.         final ExecutorService exec = Executors.newFixedThreadPool(5);   
  12.            
  13.         Callable call = new Callable(){   
  14.             public String call() throws Exception{   
  15.                 Thread.sleep(1000 * 5);   
  16.                 return "Other less important but longtime things.";   
  17.             }   
  18.         };   
  19.         Future task = exec.submit(call);   
  20.         //重要的事情   
  21.         Thread.sleep(1000 * 3);   
  22.         System.out.println("Let's do important things.");   
  23.         //其他不重要的事情   
  24.         String obj = (String)task.get();   
  25.         System.out.println(obj);   
  26.         //关闭线程池   
  27.         exec.shutdown();   
  28.     }   
  29. }  


运行结果:
Java代码 复制代码 收藏代码
  1. Let's do important things.   
  2. Other less important but longtime things.  

考虑以下场景:浏览网页时,浏览器用了5个线程下载网页中的图片文件,由于图片大小、网站访问速度等诸多因素的影响,完成图片下载的时间就会有很大的不同。先下载完成的图片就会被先显示到界面上,反之,后下载的图片就后显示。


Java的并发库的CompletionService可以满足这种场景要求。该接口有两个重要方法:submit()和take()。submit用于提交一个runnable或者callable,一般会提交给一个线程池处理;而take就是取出已经执行完毕runnable或者callable实例的Future对象,如果没有满足要求的,就等待了。 CompletionService还有一个对应的方法poll,该方法与take类似,只是不会等待,如果没有满足要求,就返回null对象
Java代码 复制代码 收藏代码
  1. package concurrent;   
  2.   
  3. import java.util.concurrent.Callable;   
  4. import java.util.concurrent.CompletionService;   
  5. import java.util.concurrent.ExecutionException;   
  6. import java.util.concurrent.ExecutorCompletionService;   
  7. import java.util.concurrent.ExecutorService;   
  8. import java.util.concurrent.Executors;   
  9. import java.util.concurrent.Future;   
  10.   
  11. public class TestCompletionService {   
  12.     public static void main(String[] args) throws InterruptedException, ExecutionException{   
  13.         ExecutorService exec = Executors.newFixedThreadPool(10);   
  14.         CompletionService serv = new ExecutorCompletionService(exec);   
  15.            
  16.         for(int index = 0; index < 5; index++){   
  17.             final int NO = index;   
  18.             Callable downImg = new Callable(){   
  19.                 public String call() throws Exception{   
  20.                     Thread.sleep((long)(Math.random() * 10000));   
  21.                     return "Downloaded Image " + NO;   
  22.                 }   
  23.             };   
  24.             serv.submit(downImg);   
  25.         }          
  26.            
  27.         Thread.sleep(1000*2);      
  28.         System.out.println("Show web content");   
  29.         for(int index=0; index<5; index++){   
  30.             Future task = serv.take();   
  31.             String img = (String)task.get();   
  32.             System.out.println(img);   
  33.         }      
  34.         System.out.println("End");   
  35.         //关闭线程池   
  36.         exec.shutdown();   
  37.     }   
  38. }  


运行结果:
Java代码 复制代码 收藏代码
  1. Show web content   
  2. Downloaded Image 2  
  3. Downloaded Image 3  
  4. Downloaded Image 0  
  5. Downloaded Image 4  
  6. Downloaded Image 1  
  7. End  

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2337946次
    • 积分:31333
    • 等级:
    • 排名:第156名
    • 原创:962篇
    • 转载:826篇
    • 译文:6篇
    • 评论:39条
    文章分类
    最新评论
    我的栏目
    我的栏目测试