Executor并发框架

一、创建线程池

Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定数目线程的线程池。

public static ExecutorService newCachedThreadPool()

创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

public static ExecutorService newSingleThreadExecutor()

创建一个单线程化的Executor。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个支持定时及周期性的任务执行的线程池。

 

java代码

  1. Executor executor = Executors.newFixedThreadPool(10);   
  2. Runnable task = new Runnable() {   
  3.     @Override  
  4.     public void run() {   
  5.         System.out.println("task over");   
  6.     }   
  7. };   
  8. executor.execute(task);   
  9.   
  10. executor = Executors.newScheduledThreadPool(10);   
  11. ScheduledExecutorService scheduler = (ScheduledExecutorService) executor;   
  12. scheduler.scheduleAtFixedRate(task, 1010, TimeUnit.SECONDS);  

二、ExecutorService与生命周期

 

ExecutorService扩展了Executor并添加了一些生命周期管理的方法。一个Executor的生命周期有三种状态,运行关闭终止 。Executor创建时处于运行状态。当调用ExecutorService.shutdown()后,处于关闭状态,isShutdown()方法返回true。这时,不应该再想Executor中添加任务,所有已添加的任务执行完毕后,Executor处于终止状态,isTerminated()返回true。

如果Executor处于关闭状态,往Executor提交任务会抛出unchecked exception RejectedExecutionException。

 

 三、使用Callable,Future返回结果

Future<V>代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask<V>实现了Future<V>和Runable<V>。Callable代表一个有返回值得操作。

Java代码
  1. Callable<Integer> func = new Callable<Integer>(){   
  2.     public Integer call() throws Exception {   
  3.         System.out.println("inside callable");   
  4.         Thread.sleep(1000);   
  5.         return new Integer(8);   
  6.     }          
  7. };         
  8. FutureTask<Integer> futureTask  = new FutureTask<Integer>(func);   
  9. Thread newThread = new Thread(futureTask);   
  10. newThread.start();   
  11.   
  12. try {   
  13.     System.out.println("blocking here");   
  14.     Integer result = futureTask.get();   
  15.     System.out.println(result);   
  16. catch (InterruptedException ignored) {   
  17. catch (ExecutionException ignored) {   
  18. }  
		Callable<Integer> func = new Callable<Integer>(){
			public Integer call() throws Exception {
				System.out.println("inside callable");
				Thread.sleep(1000);
				return new Integer(8);
			}		
		};		
		FutureTask<Integer> futureTask  = new FutureTask<Integer>(func);
		Thread newThread = new Thread(futureTask);
		newThread.start();
		
		try {
			System.out.println("blocking here");
			Integer result = futureTask.get();
			System.out.println(result);
		} catch (InterruptedException ignored) {
		} catch (ExecutionException ignored) {
		}

 ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

例子:并行计算数组的和。

Java代码
  1. package executorservice;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.List;   
  5. import java.util.concurrent.Callable;   
  6. import java.util.concurrent.ExecutionException;   
  7. import java.util.concurrent.ExecutorService;   
  8. import java.util.concurrent.Executors;   
  9. import java.util.concurrent.Future;   
  10. import java.util.concurrent.FutureTask;   
  11.   
  12. public class ConcurrentCalculator {   
  13.   
  14.     private ExecutorService exec;   
  15.     private int cpuCoreNumber;   
  16.     private List<Future<Long>> tasks = new ArrayList<Future<Long>>();   
  17.   
  18.     // 内部类   
  19.     class SumCalculator implements Callable<Long> {   
  20.         private int[] numbers;   
  21.         private int start;   
  22.         private int end;   
  23.   
  24.         public SumCalculator(final int[] numbers, int start, int end) {   
  25.             this.numbers = numbers;   
  26.             this.start = start;   
  27.             this.end = end;   
  28.         }   
  29.   
  30.         public Long call() throws Exception {   
  31.             Long sum = 0l;   
  32.             for (int i = start; i < end; i++) {   
  33.                 sum += numbers[i];   
  34.             }   
  35.             return sum;   
  36.         }   
  37.     }   
  38.   
  39.     public ConcurrentCalculator() {   
  40.         cpuCoreNumber = Runtime.getRuntime().availableProcessors();   
  41.         exec = Executors.newFixedThreadPool(cpuCoreNumber);   
  42.     }   
  43.   
  44.     public Long sum(final int[] numbers) {   
  45.         // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor   
  46.         for (int i = 0; i < cpuCoreNumber; i++) {   
  47.             int increment = numbers.length / cpuCoreNumber + 1;   
  48.             int start = increment * i;   
  49.             int end = increment * i + increment;   
  50.             if (end > numbers.length)   
  51.                 end = numbers.length;   
  52.             SumCalculator subCalc = new SumCalculator(numbers, start, end);   
  53.             FutureTask<Long> task = new FutureTask<Long>(subCalc);   
  54.             tasks.add(task);   
  55.             if (!exec.isShutdown()) {   
  56.                 exec.submit(task);   
  57.             }   
  58.         }   
  59.         return getResult();   
  60.     }   
  61.   
  62.     /**  
  63.      * 迭代每个只任务,获得部分和,相加返回  
  64.      *   
  65.      * @return  
  66.      */  
  67.     public Long getResult() {   
  68.         Long result = 0l;   
  69.         for (Future<Long> task : tasks) {   
  70.             try {   
  71.                 // 如果计算未完成则阻塞   
  72.                 Long subSum = task.get();   
  73.                 result += subSum;   
  74.             } catch (InterruptedException e) {   
  75.                 e.printStackTrace();   
  76.             } catch (ExecutionException e) {   
  77.                 e.printStackTrace();   
  78.             }   
  79.         }   
  80.         return result;   
  81.     }   
  82.   
  83.     public void close() {   
  84.         exec.shutdown();   
  85.     }   
  86. }  

四、CompletionService

在刚在的例子中,getResult()方法的实现过程中,迭代了FutureTask的数组,如果任务还没有完成则当前线程会阻塞,如果我们希望任意字任务完成后就把其结果加到result中,而不用依次等待每个任务完成,可以使CompletionService。生产者submit()执行的任务。使用者take()已完成的任务,并按照完成这些任务的顺序处理它们的结果 。也就是调用CompletionService的take方法是,会返回按完成顺序放回任务的结果,CompletionService内部维护了一个阻塞队列BlockingQueue,如果没有任务完成,take()方法也会阻塞。修改刚才的例子使用CompletionService:

Java代码
  1. public class ConcurrentCalculator2 {   
  2.   
  3.     private ExecutorService exec;   
  4.     private CompletionService<Long> completionService;   
  5.   
  6.   
  7.     private int cpuCoreNumber;   
  8.   
  9.     // 内部类   
  10.     class SumCalculator implements Callable<Long> {   
  11.         ......   
  12.     }   
  13.   
  14.     public ConcurrentCalculator2() {   
  15.         cpuCoreNumber = Runtime.getRuntime().availableProcessors();   
  16.         exec = Executors.newFixedThreadPool(cpuCoreNumber);   
  17.         completionService = new ExecutorCompletionService<Long>(exec);   
  18.   
  19.   
  20.     }   
  21.   
  22.     public Long sum(final int[] numbers) {   
  23.         // 根据CPU核心个数拆分任务,创建FutureTask并提交到Executor   
  24.         for (int i = 0; i < cpuCoreNumber; i++) {   
  25.             int increment = numbers.length / cpuCoreNumber + 1;   
  26.             int start = increment * i;   
  27.             int end = increment * i + increment;   
  28.             if (end > numbers.length)   
  29.                 end = numbers.length;   
  30.             SumCalculator subCalc = new SumCalculator(numbers, start, end);    
  31.             if (!exec.isShutdown()) {   
  32.                 completionService.submit(subCalc);   
  33.   
  34.   
  35.             }   
  36.                
  37.         }   
  38.         return getResult();   
  39.     }   
  40.   
  41.     /**  
  42.      * 迭代每个只任务,获得部分和,相加返回  
  43.      *   
  44.      * @return  
  45.      */  
  46.     public Long getResult() {   
  47.         Long result = 0l;   
  48.         for (int i = 0; i < cpuCoreNumber; i++) {               
  49.             try {   
  50.                 Long subSum = completionService.take().get();   
  51.                 result += subSum;              
  52.             } catch (InterruptedException e) {   
  53.                 e.printStackTrace();   
  54.             } catch (ExecutionException e) {   
  55.                 e.printStackTrace();   
  56.             }   
  57.         }   
  58.         return result;   
  59.     }   
  60.   
  61.     public void close() {   
  62.         exec.shutdown();   
  63.     }   
  64. }   

package com.sunjing.thread.test2;

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

/**
 * 创建ThreadPool 线程池
 * 使用了2个大小的线程池来处理100个线程
 * 需要注意的是线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程
 * @author Administrator
 *
 */
public class ThreadPoolDemo1 {

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ExecutorService exec = Executors.newFixedThreadPool(2);
  for (int index = 0; index < 100; index++) {
   Runnable run = new Runnable() {
    public void run() {
     long time = (long) (Math.random() * 1000);
     System.out.println("Sleeping" + time + "ms");
     try {
      Thread.sleep(time);
     } catch (InterruptedException e) {
     }
    }
   };
   exec.execute(run);
  }
  // must shutdown
  exec.shutdown();
 }
}

 

 

 

package com.sunjing.thread.test2;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

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

public class ExecutorScheduledServiceDemo2 {
 
 @SuppressWarnings("unchecked")
 public static void main(String[] args) {
  final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
  final Runnable beeper = new Runnable() {
   int count = 0;
   public void run() {
    System.out.println(new Date() + "beep" + (++count));
   }
  };
  // 1秒钟后运行,并每隔2秒运行一次
  final ScheduledFuture beeperHandle = scheduler.scheduleAtFixedRate(beeper, 1, 2, TimeUnit.SECONDS);
  // 2秒钟后运行,并每次在上次任务运行完后等待5秒后重新运行
  final ScheduledFuture beeperHandle2 = scheduler.scheduleWithFixedDelay(beeper, 2, 5, TimeUnit.SECONDS);
  // // 30秒后结束关闭任务,并且关闭Scheduler
  scheduler.schedule(new Runnable() {
   public void run() {
    beeperHandle.cancel(true);
    beeperHandle2.cancel(true);
    scheduler.shutdown();
   }
  }, 30, TimeUnit.SECONDS);
 }

}

 

 

 

 

package com.sunjing.thread.test2;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author Administrator
 *在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,
 *往往会等待其他线程都完成某一阶段后再执行,等所有线程都到达某一个阶段后再统一执行
 *
 *比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,
 *有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,
 *并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉
 *
 *这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,
 *另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待
 */
public class CyclicBarrierDemo3 {
 
 // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan
 private static int[] timeWalk = { 5, 8, 15, 15, 10 };
 // 自驾游
 private static int[] timeSelf = { 1, 3, 4, 4, 5 };
 // 旅游大巴
 private static int[] timeBus = { 2, 4, 6, 6, 7 };

 static String now() {
  SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
  return sdf.format(new Date()) + ": ";
 }

 static class Tour implements Runnable {
  private int[] times;
  private CyclicBarrier barrier;
  private String tourName;

  public Tour(CyclicBarrier barrier, String tourName, int[] times) {
   this.times = times;
   this.tourName = tourName;
   this.barrier = barrier;
  }

  public void run() {
   try {
    Thread.sleep(times[0] * 1000);
    System.out.println(now() + tourName + " Reached Shenzhen");
    barrier.await();
    Thread.sleep(times[1] * 1000);
    System.out.println(now() + tourName + " Reached Guangzhou");
    barrier.await();
    Thread.sleep(times[2] * 1000);
    System.out.println(now() + tourName + " Reached Shaoguan");
    barrier.await();
    Thread.sleep(times[3] * 1000);
    System.out.println(now() + tourName + " Reached Changsha");
    barrier.await();
    Thread.sleep(times[4] * 1000);
    System.out.println(now() + tourName + " Reached Wuhan");
    barrier.await();
   } catch(Exception e) {
   }
  }
 }

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  // 三个旅行团
  CyclicBarrier barrier = new CyclicBarrier(3);
  ExecutorService exec = Executors.newFixedThreadPool(3);
  exec.submit(new Tour(barrier, "WalkTour", timeWalk));
  exec.submit(new Tour(barrier, "SelfTour", timeSelf));
  exec.submit(new Tour(barrier, "BusTour", timeBus));
 
  exec.shutdown();
 }
}

 

 

 

package com.sunjing.thread.test2;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * CountDownLatch是一个倒数计数的锁,当倒数到0时触发事件,也就是开锁,其他人就可以进入了。
 * 在一些应用场合中,需要等待某个条件达到要求后才能做后面的事情;
 * 同时当线程都完成后也会触发事件,以便进行后面的操作
 *
 * CountDownLatch最重要的方法是countDown()和await(),
 * 前者主要是倒数一次,后者是等待倒数到0,如果没有到达 0,就只有阻塞等待了
 *
 * 一个CountDouwnLatch实例是不能重复使用的,也就是说它是一次性的,锁一经被打开就不能再关闭使用了
 *
 * 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束
 * @author Administrator
 *
 */
public class CountDownLatchDemo4 {

 /**
  * @param args
  * @throws InterruptedException
  */
 public static void main(String[] args) throws InterruptedException {
  // TODO Auto-generated method stub
  // 开始的倒数锁
  final CountDownLatch begin = new CountDownLatch(2);
  // 结束的倒数锁
  final CountDownLatch end = new CountDownLatch(10);
  // 十名选手
  final ExecutorService exec = Executors.newFixedThreadPool(10);
  for (int index = 0; index < 10; index++) {
   final int NO = index + 1;
   Runnable run = new Runnable() {
    public void run() {
     try {
      begin.await();
      Thread.sleep((long) (Math.random() * 10000));
      System.out.println("No." + NO + " arrived");
     } catch (InterruptedException e) {
     } finally {
      end.countDown();
     }
    }
   };
   exec.submit(run);
  }
  System.out.println("Game Start Ready" );
  begin.countDown();
  System.out.println("Game start");
  begin.countDown();
  
  end.await();
  System.out.println("Game Over");
  exec.shutdown();

 }

}

 

 

 

 

package com.sunjing.thread.test2;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 有时候在实际应用中,某些操作很耗时,但又不是不可或缺的步骤。比如用网页浏览器浏览新闻时,
 * 最重要的是要显示文字内容,至于与新闻相匹配的图片就没有那么重要的,所以此时首先保证文字信息先显示,
 * 而图片信息会后显示,但又不能不显示,由于下载图片是一个耗时的操作,所以必须一开始就得下载
 *
 * Java的并发库的Future类就可以满足这个要求。
 * Future的重要方法包括get()和cancel(),get()获取数据对象,
 * 如果数据没有加载,就会阻塞直到取到数据,而 cancel()是取消数据加载。
 * 另外一个get(timeout)操作,表示如果在timeout时间内没有取到就失败返回,而不再阻塞。
 *
 *一个非常耗时的操作必须一开始启动,但又不能一直等待;其他重要的事情又必须做,等完成后,就可以做不重要的事情
 * @author Administrator
 *
 */
public class FutureDemo5 {

 /**
  * @param args
  * @throws InterruptedException
  * @throws ExecutionException
  */
 @SuppressWarnings("unchecked")
 public static void main(String[] args) throws InterruptedException, ExecutionException {
  // TODO Auto-generated method stub
  final ExecutorService exec = Executors.newFixedThreadPool(5);
  Callable call = new Callable() {
   public String call() throws Exception {
    Thread.sleep(1000 * 5);
    return "Other less important but longtime things.";
   }
  };
  Future task = exec.submit(call);
  // 重要的事情
  Thread.sleep(1000 * 3);
  System.out.println("Let’s do important things.");
  // 其他不重要的事情
  String obj = (String) task.get();
  System.out.println(obj);
  // 关闭线程池
  exec.shutdown();
 }

}

 

 

 

package com.sunjing.thread.test2;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java并发库的Semaphore可以很轻松完成信号量控制,
 * Semaphore 可以控制某个资源可被同时访问的个数,acquire()获取一个许可,
 * 如果没有就等待,而release()释放一个许可
 * @author Administrator
 *
 */
public class SemaphoreDemo7 {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ExecutorService exec = Executors.newCachedThreadPool();
  // 只能5个线程同时访问
  final Semaphore semp = new Semaphore(5);
  // 模拟20个客户端访问
  for (int index = 0; index < 20; index++) {
   final int NO = index;
   Runnable run = new Runnable() {
    public void run() {
     try {
      // 获取许可
      semp.acquire();
      System.out.println("Accessing: " + NO);
      Thread.sleep((long) (Math.random() * 10000));
      // 访问完后,释放
      semp.release();
     } catch (InterruptedException e) {
     }
    }
   };
   exec.execute(run);
  }
  // 退出线程池
  exec.shutdown();

 }

}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值