在HotSpot VM的模型中,JAVA线程被一对一映射为本地操做系统线程。JAVA线程启动时会建立一个本地操做系统线程,当JAVA线程终止时,对应的操做系统线程也被销毁回收,而操做系统会调度全部线程并将它们分配给可用的CPU。小程序
在上层,JAVA程序会将应用分解为多个任务,而后使用应用级的调度器(Executor)将这些任务映射成固定数量的线程;在底层,操做系统内核将这些线程映射到硬件处理器上。服务器
在前面介绍的JAVA线程既是工做单元,也是执行机制。而在Executor框架中,咱们将工做单元与执行机制分离开来。Runnable和Callable是工做单元(也就是俗称的任务),而执行机制由Executor来提供。这样一来Executor是基于生产者消费者模式的,提交任务的操做至关于生成者,执行任务的线程至关于消费者。框架
一、从类图上看,Executor接口是异步任务执行框架的基础,该框架可以支持多种不一样类型的任务执行策略。dom
public interface Executor { void execute(Runnable command); }
Executor接口就提供了一个执行方法,任务是Runnbale类型,不支持Callable类型。异步
二、ExecutorService接口实现了Executor接口,主要提供了关闭线程池和submit方法:ide
public interface ExecutorService extends Executor { List<Runnable> shutdownNow(); boolean isTerminated(); <T> Future<T> submit(Callable<T> task); }
另外该接口有两个重要的实现类:ThreadPoolExecutor与ScheduledThreadPoolExecutor。spa
其中ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务;而ScheduledThreadPoolExecutor是一个实现类,能够在给定的延迟后运行任务,或者按期执行命令。操作系统
在上一篇文章中,我是使用ThreadPoolExecutor来经过给定不一样的参数从而建立本身所需的线程池,可是在后面的工做中不建议这种方式,推荐使用Exectuors工厂方法来建立线程池线程
这里先来区别线程池和线程组(ThreadGroup与ThreadPoolExecutor)这两个概念:code
a、线程组就表示一个线程的集合。
b、线程池是为线程的生命周期开销问题和资源不足问题提供解决方案,主要是用来管理线程。
Executors能够建立3种类型的ThreadPoolExecutor:SingleThreadExecutor、FixedThreadExecutor和CachedThreadPool
a、SingleThreadExecutor:单线程线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
咱们从源码来看能够知道,单线程线程池的建立也是经过ThreadPoolExecutor,里面的核心线程数和线程数都是1,而且工做队列使用的是无界队列。因为是单线程工做,每次只能处理一个任务,因此后面全部的任务都被阻塞在工做队列中,只能一个个任务执行。
b、FixedThreadExecutor:固定大小线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
这个与单线程相似,只是建立了固定大小的线程数量。
c、CachedThreadPool:无界线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
无界线程池意味着没有工做队列,任务进来就执行,线程数量不够就建立,与前面两个的区别是:空闲的线程会被回收掉,空闲的时间是60s。这个适用于执行不少短时间异步的小程序或者负载较轻的服务器。
Callable与Future是在JAVA的后续版本中引入进来的,Callable相似于Runnable接口,实现Callable接口的类与实现Runnable的类都是能够被线程执行的任务。
三者之间的关系:
Callable是Runnable封装的异步运算任务。
Future用来保存Callable异步运算的结果
FutureTask封装Future的实体类
一、Callable与Runnbale的区别
a、Callable定义的方法是call,而Runnable定义的方法是run。
b、call方法有返回值,而run方法是没有返回值的。
c、call方法能够抛出异常,而run方法不能抛出异常。
二、Future
Future表示异步计算的结果,提供了如下方法,主要是判断任务是否完成、中断任务、获取任务执行结果
1 public interface Future<V> { 2 3 boolean cancel(boolean mayInterruptIfRunning); 4 5 boolean isCancelled(); 6 7 boolean isDone(); 8 9 V get() throws InterruptedException, ExecutionException; 10 11 V get(long timeout, TimeUnit unit) 12 throws InterruptedException, ExecutionException, TimeoutException; 13 }
三、FutureTask<V>
可取消的异步计算,此类提供了对Future的基本实现,仅在计算完成时才能获取结果,若是计算还没有完成,则阻塞get方法。
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V>
FutureTask不只实现了Future接口,还实现了Runnable接口,因此不只能够将FutureTask当成一个任务交给Executor来执行,还能够经过Thread来建立一个线程。
定义一个callable的任务:
1 public class MyCallableTask implements Callable<Integer> 2 { 3 @Override 4 public Integer call() 5 throws Exception 6 { 7 System.out.println("callable do somothing"); 8 Thread.sleep(5000); 9 return new Random().nextInt(100); 10 } 11 }
1 public class CallableTest 2 { 3 public static void main(String[] args) throws Exception 4 { 5 Callable<Integer> callable = new MyCallableTask(); 6 FutureTask<Integer> future = new FutureTask<Integer>(callable); 7 Thread thread = new Thread(future); 8 thread.start(); 9 Thread.sleep(100); 10 //尝试取消对此任务的执行 11 future.cancel(true); 12 //判断是否在任务正常完成前取消 13 System.out.println("future is cancel:" + future.isCancelled()); 14 if(!future.isCancelled()) 15 { 16 System.out.println("future is cancelled"); 17 } 18 //判断任务是否已完成 19 System.out.println("future is done:" + future.isDone()); 20 if(!future.isDone()) 21 { 22 System.out.println("future get=" + future.get()); 23 } 24 else 25 { 26 //任务已完成 27 System.out.println("task is done"); 28 } 29 } 30 }
执行结果:
callable do somothing future is cancel:true future is done:true task is done
这个DEMO主要是经过调用FutureTask的状态设置的方法,演示了状态的变迁。
a、第11行,尝试取消对任务的执行,该方法若是因为任务已完成、已取消则返回false,若是可以取消还未完成的任务,则返回true,该DEMO中因为任务还在休眠状态,因此能够取消成功。
future.cancel(true);
b、第13行,判断任务取消是否成功:若是在任务正常完成前将其取消,则返回true
System.out.println("future is cancel:" + future.isCancelled());
c、第19行,判断任务是否完成:若是任务完成,则返回true,如下几种状况都属于任务完成:正常终止、异常或者取消而完成。
咱们的DEMO中,任务是因为取消而致使完成。
System.out.println("future is done:" + future.isDone());
d、在第22行,获取异步线程执行的结果,我这个DEMO中没有执行到这里,须要注意的是,future.get方法会阻塞当前线程, 直到任务执行完成返回结果为止。
System.out.println("future get=" + future.get());
public class CallableThread implements Callable<String> { @Override public String call() throws Exception { System.out.println("进入Call方法,开始休眠,休眠时间为:" + System.currentTimeMillis()); Thread.sleep(10000); return "今天停电"; } public static void main(String[] args) throws Exception { ExecutorService es = Executors.newSingleThreadExecutor(); Callable<String> call = new CallableThread(); Future<String> fu = es.submit(call); es.shutdown(); Thread.sleep(5000); System.out.println("主线程休眠5秒,当前时间" + System.currentTimeMillis()); String str = fu.get(); System.out.println("Future已拿到数据,str=" + str + ";当前时间为:" + System.currentTimeMillis()); } }
执行结果:
进入Call方法,开始休眠,休眠时间为:1478606602676
主线程休眠5秒,当前时间1478606608676
Future已拿到数据,str=今天停电;当前时间为:1478606612677
这里的future是直接扔到线程池里面去执行的。因为要打印任务的执行结果,因此从执行结果来看,主线程虽然休眠了5s,可是从Call方法执行到拿到任务的结果,这中间的时间差正好是10s,说明get方法会阻塞当前线程直到任务完成。
经过FutureTask也能够达到一样的效果:
public static void main(String[] args) throws Exception { ExecutorService es = Executors.newSingleThreadExecutor(); Callable<String> call = new CallableThread(); FutureTask<String> task = new FutureTask<String>(call); es.submit(task); es.shutdown(); Thread.sleep(5000); System.out.println("主线程等待5秒,当前时间为:" + System.currentTimeMillis()); String str = task.get(); System.out.println("Future已拿到数据,str=" + str + ";当前时间为:" + System.currentTimeMillis()); }
以上的组合能够给咱们带来这样的一些变化:
若有一种场景中,方法A返回一个数据须要10s,A方法后面的代码运行须要20s,可是这20s的执行过程当中,只有后面10s依赖于方法A执行的结果。若是与以往同样采用同步的方式,势必会有10s的时间被浪费,若是采用前面两种组合,则效率会提升:
一、先把A方法的内容放到Callable实现类的call()方法中
二、在主线程中经过线程池执行A任务
三、执行后面方法中10秒不依赖方法A运行结果的代码
四、获取方法A的运行结果,执行后面方法中10秒依赖方法A运行结果的代码
这样代码执行效率一会儿就提升了,程序没必要卡在A方法处。