线程并发库

Java 5 添加了一个新的包到  Java 平台,java.util.concurrent 包。这个包包含有一系列能够让  Java 的并发

编程变得更加简单轻松的类。在这个包被添加以前,你需要自己去动手实现自己的相关工具类。下面带你认识下

java.util.concurrent包里的这些类,然后你可以尝试着如何在项目中使用它们。本文中将使用  Java 6 版本,我不确定这和  Java 5 版本里的是否有一些差异。我不会去解释关于  Java 并发的核心问题 – 其背后的原理,也就是说,如果你对那些东西感兴趣, 

( 1 )  Java线 

Java5的多线程并有两个大发库在java.util.concurrent包及子包中,子包主要的包有一下两个 
1)  java.util.concurrent包  (多线
➢  java.util.concurrent  包含许多线程安全、测试良好、高性能的并发构建块。不客气地说,创建 java.util.concurrent 的目的就是要实现  Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性,后面、我们会做介绍。 

➢ 如果一些类名看起来相似,可能是因为  java.util.concurrent  中的许多概念源自  Doug  Lea  的 util.concurrent 库。 

2)  java.util.concurrent.atomic包  (线子性操作
➢ 查看atomic包文档页下面的介绍,它可以对多线程的基本数据、数组中的基本数据和对象中的基本数据进行多线程的操作(AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpDater…) 
➢ 通过如下两个方法快速理解atomic包的意义: 
n AtomicInteger类的boolean compareAndSet(expectedValue, updateValue);
n AtomicIntegerArray类的int addAndGet(int i, int delta);

➢ 顺带解释volatile类型的作用,需要查看java语言规范。 

n volatile 修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。(具有可见性) n volatile没有原子性。 

3)  java.util.concurrent.lock包  (线程

为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。该框架允许更灵活地使用锁和条件。

本包下有三大接口,下面简单介绍下: 
➢ Lock 接口:支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括  hand-over-hand 和锁重排算法)中使用这些规则。主要的实现是  ReentrantLock。 

➢ ReadWriteLock 接口:以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实现,即  ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。 

➢ Condition 接口:描述了可能会与锁有关联的条件变量。这些变量在用法上与使用  Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个  Lock 可能与多个  Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的  Object 版本中的不同。 

( 2 )  Java入门 

下面我们将分别介绍java.util.concurrent包下的常用类的使用。 

1)  java.util.concurrent  

  java.util.concurrent包描述: 
在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类。此包下有一些组件,其中包括: 

l  执行程序(线程池) 

l  并发队列 

l  同步器 

l  并发Collocation 

下面我们将java.util.concurrent包下的组件逐一简单介绍: 

A. 执行程序 

➢ Executors线程池工 

首次我们来说下线程池的作用: 

 线程池作用就是限制系统中执行线程的数量。 

 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程 排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程 池中有等待的工作线程,就可以开始运行了;否则进入等待队列。 

为什么要用线程池: 

l  减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务 

l  可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机) 

Executors详解: 
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是 ExecutorService。ThreadPoolExecutor 是 Executors 类的底层实现。我们先介绍下Executors。 

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反

复创建线程对象所带来的性能开销,节省了系统的资源。 

Java5中并发库中,线程池创建线程大致可以分为下面三种: 

//创建固定大小的线程池 

ExecutorService fPool = Executors.newFixedThreadPool(3); 

//创建缓存大小的线程池 

ExecutorService cPool = Executors.newCachedThreadPool(); 

//创建单一的线程池 
ExecutorService sPool = Executors.newSingleThreadExecutor(); 

下面我们通过简单示例来分别说明: 

固定 

import java.util.concurrent.Executors; 

  import java.util.concurrent.ExecutorService;   

  /**  

  * Java线程:线程池-  

  *  

  * @author Administrator 2009-11-4 23:30:44  

  */   

  public class Test {   

     public static void main(String[] args) {   

      //创建一个可重用固定线程数的线程池   
      ExecutorService pool = Executors.newFixedThreadPool(2);         //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口         Thread t1 = new MyThread();   

      Thread t2 = new MyThread();   

      Thread t3 = new MyThread();   

      Thread t4 = new MyThread();   

     Thread t5 = new MyThread();   

     //将线程放入池中进行执行   

     pool.execute(t1);   

     pool.execute(t2);   

     pool.execute(t3);   

       pool.execute(t4);

     pool.execute(t5);   

       //关闭线程池 

    pool.shutdown();   

    }   

  }   

  class MyThread extends Thread{   

    @Override   

    public void run() {   
      System.out.println(Thread.currentThread().getName()+"正在执行。。。");       }   

  }   

运行结果: 

pool-1-thread-1正在执行。。。 

   pool-1-thread-1正在执行。。。  

   pool-1-thread-2正在执行。。。  

   pool-1-thread-1正在执行。。。  

   pool-1-thread-2正在执行。。。  

从上面的运行来看,我们 Thread 类都是在线程池中运行的,线程池在执行 execute 方法来执行 Thread 类

中的run方法。不管execute执行几次,线程池始终都会使用2个线程来处理。不会再去创建出其他线程来处理

run方法执行。这就是固定大小线程池。 

单任 

我们将上面的代码 

//创建一个可重用固定线程数的线程池 

    ExecutorService pool = Executors.newFixedThreadPool(2); 

 改为: 
 //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 

 ExecutorService pool = Executors.newSingleThreadExecutor();  

运行结果: 

pool-1-thread-1正在执行。。。 

   pool-1-thread-1正在执行。。。  

   pool-1-thread-1正在执行。。。  

   pool-1-thread-1正在执行。。。  

   pool-1-thread-1正在执行。。。  

运行结果看出,单任务线程池在执行execute方法来执行Thread类中的run方法。不管execute执行几次,线程池

始终都会使用单个线程来处理。 

补充java线线为死线线就没次启会出

异常息:Exception in thread "main" java.lang.IllegalThreadStateException这个呢? 

我们使Executors.newSingleThreadExecutor()来再个线试) 

可变 

//创建一个可重用固定线程数的线程池 

    ExecutorService pool = Executors.newFixedThreadPool(2); 

 改为: 
 //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 

ExecutorService pool = Executors.newCachedThreadPool(); 

运行结果: 

pool-1-thread-5正在执行。。。 

     pool-1-thread-1正在执行。。。 

     pool-1-thread-4正在执行。。。 

     pool-1-thread-3正在执行。。。 

   pool-1-thread-2正在执行。。。  

运行结果看出,可变任务线程池在执行 execute 方法来执行 Thread 类中的 run 方法。这里 execute 执行多次,

线程池就会创建出多个线程来处理 Thread 类中 run 方法。所有我们看到连接池会根据执行的情况,在程序运行时创

建多个线程来处理,这里就是可变连接池的特点。 

那么在上面的三种创建方式,Executors 还可以在执行某个线程时,定时操作。那么下面我们通过代码简单演示

下。 

延迟 

import java.util.concurrent.Executors; 

  import java.util.concurrent.ScheduledExecutorService;   

  import java.util.concurrent.TimeUnit;   

  /**  

  * Java线程:线程池-  

  *  

  * @author Administrator 2009-11-4 23:30:44  

  */   

  public class Test {   

  public static void main(String[] args) {   

  //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。   

  ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);     //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口   
  Thread t1 = new MyThread();   

  Thread t2 = new MyThread();   

  Thread t3 = new MyThread();   

  Thread t4 = new MyThread();   

  Thread t5 = new MyThread();   

  //将线程放入池中进行执行   

  pool.execute(t1);   

  pool.execute(t2);   

  pool.execute(t3);   

  //使用定时执行风格的方法   
  pool.schedule(t4, 10, TimeUnit.MILLISECONDS);  //t4和t5在10秒后执行   pool.schedule(t5, 10, TimeUnit.MILLISECONDS);   

  //关闭线程池   

  pool.shutdown();   

  }   

  }   

  class MyThread extends Thread {   

   @Override   

     public void run() {
     System.out.println(Thread.currentThread().getName() + "正在执行。。。");      }   

  }   

运行结果: 

pool-1-thread-1正在执行。。。 

  pool-1-thread-2正在执行。。。  

  pool-1-thread-1正在执行。。。  

  pool-1-thread-1正在执行。。。  

  pool-1-thread-2正在执行。。。  

➢ ExecutorService执行器服 

java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务。因此一

个  ExecutorService 很类似于一个线程池。实际上,存在于  java.util.concurrent 包里的  ExecutorService 实

现就是一个线程池实现。 

 ExecutorService例子: 

以下是一个简单的  ExecutorService 例子: 

//线程工厂类创建出线程池 
ExecutorService executorService = Executors.newFixedThreadPool(10);

//执行一个线程任务 

executorService.execute(new Runnable() { 

 public void run() {   

 System.out.println("Asynchronous task");   

 }   

});

//线程池关闭 

executorService.shutdown(); 

上面代码首先使用  newFixedThreadPool() 工厂方法创建一个  ExecutorService。这里创建了一个十个线程执行任务

的线程池。然后,将一个  Runnable 接口的匿名实现类传递给  execute() 方法。这将导致  ExecutorService 中的某

个线程执行该  Runnable。这里可以看成一个,示例代码中的任务分派我们可以理解为: 

一个线给一  ExecutorService  

一旦线  ExecutorService线程将独立行。 

如下图: 

Thread1

Operation 1

 ExecutorService实现: 
既然  ExecutorService 是个接口,如果你想用它的话就得去使用它的实现类之一。 

java.util.concurrent 包提供了  ExecutorService 接口的以下实现类: 

l  ThreadPoolExecutor 

l  ScheduledThreadPoolExecutor 

 ExecutorService创建: 
ExecutorService  的创建依赖于你使用的具体实现。但是你也可以使用  Executors  工厂类来创建 ExecutorService 实例。代码示例: 

ExecutorService executorService1 = Executors.newSingleThreadExecutor();  //之前Executors已介绍 

ExecutorService executorService2 = Executors.newFixedThreadPool(10); 

ExecutorService executorService3 = Executors.newScheduledThreadPool(10); 

 ExecutorService使用: 

有几种不同的方式来将任务委托给  ExecutorService 去执行: 

l  execute(Runnable) 

l  submit(Runnable) 

l  submit(Callable) 

l  invokeAny(…) 

l  invokeAll(…) 

接下来我们挨个看一下这些方法。 

✓  execute(Runnable) 

execute(Runnable)  方法要求一个  java.lang.Runnable  对象,然后对它进行异步执行。以下是使用 

ExecutorService 执行一个  Runnable 的示例: 

//从Executors中获得ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor(); //执行 ExecutorService 中的方法 

executorService.execute(new Runnable() { 

 public void run() {   

 System.out.println("Asynchronous task");   

 }   

});

//线程池关闭 

executorService.shutdown(); 

特点:没有办法得知被执行的  Runnable 的执行结果。如果有需要的话你得使用一个  Callable(以下将做介

绍)。 

✓  submit(Runnable) 

submit(Runnable) 方法也要求一个  Runnable 实现类,但它返回一个  Future 对象。这个  Future 对象可

以用来检查  Runnable 是否已经执行完毕。以下是  ExecutorService submit() 示例: 

//从Executors中获得ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor(); 

Future future = executorService.submit(new Runnable() { 

 public void run() {   

 System.out.println("Asynchronous task");   

 }   

});

future.get();  //获得执行完run方法后的返回值,这里使用的Runnable,所以这里没有返回值,返回的是null。 executorService.shutdown(); 

✓  submit(Runnable) 

submit(Callable) 方法类似于  submit(Runnable) 方法,除了它所要求的参数类型之外。Callable 实例除了

它的  call() 方法能够返回一个结果之外和一个  Runnable 很相像。Runnable.run() 不能够返回一个结果 。

Callable 的结果可以通过  submit(Callable) 方法返回的  Future 对象进行获取。 

以下是一个  ExecutorService Callable 示例: 

//从Executors中获得ExecutorService
ExecutorService executorService = Executors.newSingleThreadExecutor(); 

Future future = executorService.submit(new Callable(){ 

 public Object call() throws Exception {   

 System.out.println("Asynchronous Callable");   

 return "Callable Result";   

 }   

});

System.out.println("future.get() = " + future.get()); 

executorService.shutdown(); 

输出: 

Asynchronous Callable

future.get() = Callable Result 

✓  invokeAny() 

invokeAny() 方法要求一系列的  Callable 或者其子接口的实例对象。调用这个方法并不会返回一个  Future ,

但它返回其中一个  Callable 对象的结果。无法保证返回的是哪个  Callable 的结果 – 只能表明其中一个已

执行结束。 

如果其中一个任务执行结束(或者抛了一个异常),其他  Callable 将被取消。以下是示例代码: 

ExecutorService executorService = Executors.newSingleThreadExecutor(); 

Set> callables = new HashSet>(); 

callables.add(new Callable() { 

 public String call() throws Exception {   

 return "Task 1";   

 }   

});

callables.add(new Callable() { 

 public String call() throws Exception {   

 return "Task 2";   

 }   

});

callables.add(new Callable() { 

 public String call() throws Exception {   

 return "Task 3";   

 }   

});

String result = executorService.invokeAny(callables); 

System.out.println("result = " + result); 

executorService.shutdown(); 

上述代码将会打印出给定  Callable 集合中的一个的执行结果。我自己试着执行了它几次,结果始终在变。有

时是 “Task 1″,有时是 “Task 2″ 等等。 

✓  invokeAll() 

invokeAll() 方法将调用你在集合中传给  ExecutorService 的所有  Callable 对象。invokeAll() 返回一系列

的  Future 对象,通过它们你可以获取每个  Callable 的执行结果。 

记住,一个任务可能会由于一个异常而结束,因此它可能没有  "成功"。无法通过一个  Future 对象来告知我

们是两种结束中的哪一种。 

以下是一个代码示例: 

ExecutorService executorService = Executors.newSingleThreadExecutor();

Set> callables = new HashSet>();

callables.add(new Callable() {

 public String call() throws Exception {   

 return "Task 1";   

 }   

});

callables.add(new Callable() {

 public String call() throws Exception {   

 return "Task 2";   

 }   

});

callables.add(new Callable() {

 public String call() throws Exception {   

 return "Task 3";   

 }   

});

List> futures = executorService.invokeAll(callables);

for(Future future : futures){

 System.out.println("future.get = " + future.get());   

}

executorService.shutdown(); 

输出结果: 

future.get = Task 3

future.get = Task 1

future.get = Task 2 

 Executors: 

使用shutdown和shutdownNow可以关闭线程池 

两者的区别: 

shutdown只是将空闲的线程interrupt() 了,shutdown()之前提交的任务可以继续执行直到结束。 

shutdownNow 是 interrupt 所有线程, 因此大部分线程将立刻被中断。之所以是大部分,而不是全部 ,

是因为interrupt()方法能力有限。 

➢ ThreadPoolExecutor线程池执行者 

java.util.concurrent.ThreadPoolExecutor 是  ExecutorService 接口的一个实现。ThreadPoolExecutor 使用其内部池中的线程执行给定任务(Callable 或者  Runnable)。 

ThreadPoolExecutor 包含的线程池能够包含不同数量的线程。池中线程的数量由以下变量决定: 

l corePoolSize 

l maximumPoolSize 
当一个任务委托给线程池时,如果池中线程数量低于  corePoolSize,一个新的线程将被创建,即使池中可能尚有空闲线程。如果内部任务队列已满,而且有至少  corePoolSize  正在运行,但是运行线程的数量低于 maximumPoolSize,一个新的线程将被创建去执行该任务。 

ThreadPoolExecutor 图解: 

Thread Pool Executor

Task Queue

Thread

maxPoolSize

Task

Thread

corePoolSize

Task

Thread

Task

Thread

 ThreadPoolExecutor: 

int  corePoolSize  =    5; 

int  maxPoolSize   =   10; 

long keepAliveTime = 5000; 

ExecutorService threadPoolExecutor =

 new ThreadPoolExecutor(   

 corePoolSize,   

 maxPoolSize,   

 keepAliveTime,   

 TimeUnit.MILLISECONDS,   

 new LinkedBlockingQueue()   

 );   

构造方法参数列表解释: 

corePoolSize - 池中所保存的线程数,包括空闲线程。 

maximumPoolSize - 池中允许的最大线程数。 

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。 unit - keepAliveTime 参数的时间单位。 

workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。 

➢ ScheduledPoolExecutor定时线程池执行者 

java.util.concurrent.ScheduledExecutorService 是一个  ExecutorService, 它能够将任务延后执行,或者间隔

固定时间多次执行。 任务由一个工作者线程异步执行,而不是由提交任务给  ScheduledExecutorService 的那个线

程执行。 

ScheduledPoolExecutor例子: 

ScheduledExecutorService scheduledExecutorService =

 Executors.newScheduledThreadPool(5);   

ScheduledFuture scheduledFuture =

 scheduledExecutorService.schedule(new Callable() {   

 public Object call() throws Exception {   

 System.out.println("Executed!");   

 return "Called!";   

 }   

 },   

 5,   

 TimeUnit.SECONDS);//5秒后执行 

首先一个内置  5 个线程的  ScheduledExecutorService 被创建。之后一个  Callable 接口的匿名类示例被创建然后传递给  schedule() 方法。后边的俩参数定义了  Callable 将在  5 秒钟之后被执行。 

ScheduledExecutorService实现: 
ScheduledExecutorService 是一个接口,你要用它的话就得使用  java.util.concurrent 包里对它的某个实现类。

ScheduledExecutorService 具有以下实现类:ScheduledThreadPoolExecutor 

创建ScheduledExecutorService: 
如何创建一个  ScheduledExecutorService 取决于你采用的它的实现类。但是你也可以使用  Executors 工厂类来创建一个  ScheduledExecutorService 实例。比如: 

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

ScheduledExecutorService使用: 
一旦你创建了一个  ScheduledExecutorService,你可以通过调用它的以下方法: 

l  schedule (Callable task, long delay, TimeUnit timeunit)

l  schedule (Runnable task, long delay, TimeUnit timeunit) 

l  scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

l  scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit) 下面我们就简单看一下这些方法。 

✓  schedule (Callable task, long delay, TimeUnit timeunit) 

ScheduledExecutorService scheduledExecutorService =

 Executors.newScheduledThreadPool(5);   

ScheduledFuture scheduledFuture =

 scheduledExecutorService.schedule(new Callable() {   

 public Object call() throws Exception {   

 System.out.println("Executed!");   

 return "Called!";   

 }   

 },   

 5,   

 TimeUnit.SECONDS);   

System.out.println("result = " + scheduledFuture.get());

scheduledExecutorService.shutdown(); 

输出结果: 

Executed!

result = Called!

✓  schedule (Runnable task, long delay, TimeUnit timeunit) 
这一方法规划一个任务将被定期执行。该任务将会在首个  initialDelay 之后得到执行,然后每个  period 时间之后重复执行。 

如果给定任务的执行抛出了异常,该任务将不再执行。如果没有任何异常的话,这个任务将会持续循环执行到 ScheduledExecutorService 被关闭。 

如果一个任务占用了比计划的时间间隔更长的时候,下一次执行将在当前执行结束执行才开始。计划任务在同一时间不会有多个线程同时执行。 

✓  scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
这一方法规划一个任务将被定期执行。该任务将会在首个  initialDelay 之后得到执行,然后每个  period 时间之后重复执行。 

如果给定任务的执行抛出了异常,该任务将不再执行。如果没有任何异常的话,这个任务将会持续循环执行到 ScheduledExecutorService 被关闭。 

如果一个任务占用了比计划的时间间隔更长的时候,下一次执行将在当前执行结束执行才开始。计划任务在同一时间不会有多个线程同时执行。 

✓  scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)  除了  period 有不同的解释之外这个方法和  scheduleAtFixedRate() 非常像。scheduleAtFixedRate() 方法中,period 被解释为前一个执行的开始和下一个执行的开始之间的间隔时间。而在本方法中,period 则被解释为前一个执行的结束和下一个执行的结束之间的间隔。因此这个延迟是执行结束之间的间隔,而不是执行开始之间的间隔。 

ScheduledExecutorService关闭: 
正如  ExecutorService,在你使用结束之后你需要把  ScheduledExecutorService 关闭掉。否则他将导致  JVM 继续运行,即使所有其他线程已经全被关闭。 

你 可 以 使 用 从   ExecutorService  接 口 继 承 来 的   shutdown()  或   shutdownNow()  方 法 将 ScheduledExecutorService 关闭。参见  ExecutorService 关闭部分以获取更多信息。 

➢ ForkJoinPool合并分叉(线程池) 

ForkJoinPool 在  Java 7 中被引入。它和 ExecutorService 很相似,除了一点不同。ForkJoinPool 让我们可以很方便地把任务分裂成几个更小的任务,这些分裂出来的任务也将会提交给  ForkJoinPool。任务可以继续分割成更小的子任务,只要它还能分割。可能听起来有些抽象,因此本节中我们将会解释  ForkJoinPool 是如何工作的,还有任务分割是如何进行的。 

合并: 
在我们开始看  ForkJoinPool 之前我们先来简要解释一下分叉和合并的原理。分叉和合并原理包含两个递归进行的步骤。两个步骤分别是分叉步骤和合并步骤。 

分叉: 
一个使用了分叉和合并原理的任务可以将自己分叉(分割)为更小的子任务,这些子任务可以被并发执行。

通过把自己分割成多个子任务,每个子任务可以由不同的  CPU 并行执行,或者被同一个  CPU 上的不同线程执行。只有当给的任务过大,把它分割成几个子任务才有意义。把任务分割成子任务有一定开销,因此对于小型任务,这个分割的消耗可能比每个子任务并发执行的消耗还要大。 

什么时候把一个任务分割成子任务是有意义的,这个界限也称作一个阀值。这要看每个任务对有意义阀值的决定。很大程度上取决于它要做的工作的种类。 

合并: 
当一个任务将自己分割成若干子任务之后,该任务将进入等待所有子任务的结束之中。一旦子任务执行结束,该任务可以把所有结果合并到同一个结果。

当然,并非所有类型的任务都会返回一个结果。如果这个任务并不返回一个结果,它只需等待所有子任务执行完毕。也就不需要结果的合并啦。 

所以我们可以将 ForkJoinPool 是一个特殊的线程池,它的设计是为了更好的配合 分叉-和-合并 任务分割的工作。ForkJoinPool 也在  java.util.concurrent 包中,其完整类名为  java.util.concurrent.ForkJoinPool。 

创建ForkJoinPool: 
你可以通过其构造子创建一个  ForkJoinPool。作为传递给  ForkJoinPool 构造子的一个参数,你可以定义你期望的并行级别。并行级别表示你想要传递给  ForkJoinPool 的任务所需的线程或  CPU 数量。以下是一个  ForkJoinPool 示例: 

//创建了一个并行级别为 4 的 ForkJoinPool 

ForkJoinPool forkJoinPool = new ForkJoinPool(4);

提交任务ForkJoinPool: 
就像提交任务到  ExecutorService 那样,把任务提交到  ForkJoinPool。你可以提交两种类型的任务。一种是没有任何返回值的(一个 “行动”),另一种是有返回值的(一个”任务”)。这两种类型分别由  RecursiveAction 和 RecursiveTask 表示。接下来介绍如何使用这两种类型的任务,以及如何对它们进行提交。 

RecursiveAction: 
RecursiveAction 是一种没有任何返回值的任务。它只是做一些工作,比如写数据到磁盘,然后就退出了。一个 RecursiveAction 可以把自己的工作分割成更小的几块,这样它们可以由独立的线程或者  CPU 执行。你可以通过继承来实现一个  RecursiveAction。示例如下: 

import java.util.ArrayList; 

import java.util.List;

import java.util.concurrent.RecursiveAction; 

public class MyRecursiveAction extends RecursiveAction {

 private long workLoad = 0;   

 public MyRecursiveAction(long workLoad) {   

 this.workLoad = workLoad;   

 }   

 protected void compute() {   

 //if work is above threshold, break tasks up into smaller tasks    //翻译:如果工作超过门槛,把任务分解成更小的任务 

 if(this.workLoad > 16) {   
 System.out.println("Splitting workLoad : " + this.workLoad);   

 List subtasks =   

 new ArrayList();   

 subtasks.addAll(createSubtasks());   

 for(RecursiveAction subtask : subtasks){   

 subtask.fork();   

 }   

 } else {   
 System.out.println("Doing workLoad myself: " + this.workLoad);    }   

 }   

 private List createSubtasks() {   

 List subtasks =   

 new ArrayList();   

 MyRecursiveAction subtask1 = new MyRecursiveAction(this.workLoad / 2);    MyRecursiveAction subtask2 = new MyRecursiveAction(this.workLoad / 2);   

 subtasks.add(subtask1);   

 subtasks.add(subtask2);   

 return subtasks;   

 }   

}

例子很简单。MyRecursiveAction 将一个虚构的  workLoad 作为参数传给自己的构造子。如果  workLoad 高于

一个特定阀值,该工作将被分割为几个子工作,子工作继续分割。如果  workLoad 低于特定阀值,该工作将由 

MyRecursiveAction 自己执行。你可以这样规划一个  MyRecursiveAction 的执行: 

//创建了一个并行级别为 4 的 ForkJoinPool 

ForkJoinPool forkJoinPool = new ForkJoinPool(4); 

//创建一个没有返回值的任务 
MyRecursiveAction myRecursiveAction = new MyRecursiveAction(24); 

//ForkJoinPool执行任务 

forkJoinPool.invoke(myRecursiveAction); 

运行结果: 

Splitting workLoad : 24

Doing workLoad myself: 12 

RecursiveTask: 

RecursiveTask 是一种会返回结果的任务。它可以将自己的工作分割为若干更小任务,并将这些子任务的执行结

果合并到一个集体结果。可以有几个水平的分割和合并。以下是一个  RecursiveTask 示例: 

import java.util.ArrayList; 

import java.util.List;

import java.util.concurrent.RecursiveTask; 

public class MyRecursiveTask extends RecursiveTask {

 private long workLoad = 0;   

 public MyRecursiveTask(long workLoad) {   

 this.workLoad = workLoad;   

 }   

 protected Long compute() {   

 //if work is above threshold, break tasks up into smaller tasks    if(this.workLoad > 16) {   
 System.out.println("Splitting workLoad : " + this.workLoad);   

 List subtasks =   

 new ArrayList();   

 subtasks.addAll(createSubtasks());   

 for(MyRecursiveTask subtask : subtasks){   

 subtask.fork();   

 }   

 long result = 0;   

 for(MyRecursiveTask subtask : subtasks) {   

 result += subtask.join();   

 }   

 return result;   

 } else {   
 System.out.println("Doing workLoad myself: " + this.workLoad);    return workLoad * 3;   

 }   

 }   

 private List createSubtasks() {   

 List subtasks =   

 new ArrayList();   

 MyRecursiveTask subtask1 = new MyRecursiveTask(this.workLoad / 2);    MyRecursiveTask subtask2 = new MyRecursiveTask(this.workLoad / 2);   

 subtasks.add(subtask1);   

 subtasks.add(subtask2);   

 return subtasks;   

 }   

}

除了有一个结果返回之外,这个示例和  RecursiveAction  的例子很像。MyRecursiveTask  类继承自 

RecursiveTask,这也就意味着它将返回一个  Long 类型的结果。 

MyRecursiveTask 示例也会将工作分割为子任务,并通过  fork() 方法对这些子任务计划执行。此外,本示例还

通过调用每个子任务的  join() 方法收集它们返回的结果。子任务的结果随后被合并到一个更大的结果,并最终将其返

回。对于不同级别的递归,这种子任务的结果合并可能会发生递归。 

你可以这样规划一个  RecursiveTask: 

//创建了一个并行级别为 4 的 ForkJoinPool 

ForkJoinPool forkJoinPool = new ForkJoinPool(4); 

//创建一个有返回值的任务 

MyRecursiveTask myRecursiveTask = new MyRecursiveTask(128); 

//线程池执行并返回结果 

long mergedResult = forkJoinPool.invoke(myRecursiveTask); 

System.out.println("mergedResult = " + mergedResult); 

注意: ForkJoinPool.invoke() 方法的调用来获取最终执行结果的。 

你可能感兴趣的:(java,jvm,面试)