并发编程之Executor,Executors,ExecutorService和ThreadPoolExecutor

学习java并发编程,一定要对线程池非常了解,这样才能更好的管理线程,使用线程,从而提升效率。但是,对于初学者往往对Executor,Executors,ExecutorService,ThreadPoolExecutor傻傻分不清,小文特别梳理总结了,用于工作学习。

区分Executor,Executors及其ExecutorService:

Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent包下,通过该框架来控制线程的启动、执行和关闭,可以简化

并发编程的操作。

Executor接口,提供方法execute(Runnable command),该方法接收一个Runable实例。因此,该接口也成为base接口。

Executors接口,提供工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。通过Executors的以上五个静态工厂方法获得ExecutorService实例,

而后调用该实例的execute(Runnable command)方法即可。

五个静态工厂方法:

ExecutorService executorService = Executors.newCachedThreadPool(); //执行生存期很短的异步型任务,默认周期是60s,超过线程实例将被终止及移出池。

ExecutorService executorService = Executors.newFixedThreadPool(int); //只能有固定数目的活动线程存在,长连接

ExecutorService executorService = Executors.newSingleThreadExecutor(); //只能有一个线程,长连接

ExecutorService executorService = Executors.newScheduledThreadPool(int); //线程可以按schedule依次delay执行,或周期执行,结合Timer

ExecutorService executorService = Executors.newSingleThreadExecutor(); //创建一个单线程化的支持定时的线程池,可以用一个线程周期性执行任务

ExecutorService接口继承自Executor接口,提供了更丰富的实现多线程的方法,用该接口来实现和管理多线程。

ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。

例:Executor执行Runnable任务

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

public class TestCachedThreadPool{

public static void main(String[] args){

ExecutorService executorService = Executors.newCachedThreadPool();

// ExecutorService executorService = Executors.newFixedThreadPool(5);

// ExecutorService executorService = Executors.newSingleThreadExecutor();

for (int i = 0; i < 5; i++){

executorService.execute(new TestRunnable());

System.out.println("************* a" + i + " *************");

}

executorService.shutdown();

}

}

class TestRunnable implements Runnable{

public void run(){

System.out.println(Thread.currentThread().getName() + "线程被调用了。");

}

}

Executor执行Callable任务,Callable的call()方法只能通过ExecutorService的submit(Callable task) 方法来执行,并且返回一个 Future,是表示任务等待完成的 Future。

例2:

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.*;

public class CallableDemo{

public static void main(String[] args){

ExecutorService executorService = Executors.newCachedThreadPool();

List resultList = new ArrayList();

//创建10个任务并执行

for (int i = 0; i < 10; i++){

//使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中

Future future = executorService.submit(new TaskWithResult(i));

//将任务执行结果存储到List中

resultList.add(future);

}

//遍历任务的结果

for (Future fs : resultList){

try{

while(!fs.isDone);//Future返回如果没有完成,则一直循环等待,直到Future返回完成

System.out.println(fs.get()); //打印各个线程(任务)执行的结果

}catch(InterruptedException e){

e.printStackTrace();

}catch(ExecutionException e){

e.printStackTrace();

}finally{

//启动一次顺序关闭,执行以前提交的任务,但不接受新任务

executorService.shutdown();

}

}

}

}

class TaskWithResult implements Callable{

private int id;

public TaskWithResult(int id){

this.id = id;

}

/**

* 任务的具体过程,一旦任务传给ExecutorService的submit方法,

* 则该方法自动在一个线程上执行

*/

public String call() throws Exception {

System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());

//该返回结果将被Future的get方法得到

return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();

}

}

ThreadPoolExecutor类: 常用于自定义线程池

public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue workQueue)

参数说明:

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

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

keepAliveTime:线程池中的空闲线程所能持续的最长时间。

unit:持续时间的单位。

workQueue:任务执行前保存任务的队列,仅保存由execute方法提交的Runnable任务。

排队的策略:

1、直接提交。缓冲队列采用 SynchronousQueue,它将任务直接交给线程处理而不保持它们。如果不存在可用于立即运行任务的线程(即线程池中的线程都在工作),则试图把任务加入缓冲队列将会失败,因此会构造一个新的线程来处理新添加的任务,并将其加入到线程池中。直接提交通常要求无界 maximumPoolSizes(Integer.MAX_VALUE) 以避免拒绝新提交的任务。newCachedThreadPool采用的便是这种策略。

2、无界队列。使用无界队列(典型的便是采用预定义容量的 LinkedBlockingQueue,理论上是该缓冲队列可以对无限多的任务排队)将导致在所有 corePoolSize 线程都工作的情况下将新任务加入到缓冲队列中。这样,创建的线程就不会超过 corePoolSize,也因此,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列。newFixedThreadPool采用的便是这种策略。

3、有界队列。当使用有限的 maximumPoolSizes 时,有界队列(一般缓冲队列使用ArrayBlockingQueue,并制定队列的最大长度)有助于防止资源耗尽,但是可能较难调整和控制,队列大小和最大池大小需要相互折衷,需要设定合理的参数。

案例:

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.BlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

public class ThreadPoolTest{

public static void main(String[] args){

//创建等待队列

BlockingQueue bqueue = new ArrayBlockingQueue(20);

//创建线程池,池中保存的线程数为3,允许的最大线程数为5

ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50,TimeUnit.MILLISECONDS,bqueue);

//创建七个任务

Runnable t1 = new MyThread();

Runnable t2 = new MyThread();

Runnable t3 = new MyThread();

Runnable t4 = new MyThread();

Runnable t5 = new MyThread();

Runnable t6 = new MyThread();

Runnable t7 = new MyThread();

//每个任务会在一个线程上执行

pool.execute(t1);

pool.execute(t2);

pool.execute(t3);

pool.execute(t4);

pool.execute(t5);

pool.execute(t6);

pool.execute(t7);

//关闭线程池

pool.shutdown();

}

}

class MyThread implements Runnable{

@Override

public void run(){

System.out.println(Thread.currentThread().getName() + "正在执行。。。");

try{

Thread.sleep(100);

}catch(InterruptedException e){

e.printStackTrace();

}

}

}

输出结果:

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

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

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

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

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

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20171228G06E5D00?refer=cp_1026

同媒体快讯

  • git常用命令

    2018-10-23

相关快讯

扫码关注云+社区