ThreadPoolExecutor入门

losetowin 发布于:2017-3-22 0:37 分类:Java  有 864 人浏览,获得评论 0 条 标签: threadpoolexecutor 

本文地址:http://www.dutycode.com/threadpoolexecutor_rumen.html
除非注明,文章均为 www.dutycode.com 原创,欢迎转载!转载请注明本文地址,谢谢。
01

为什么要用线程池?

1、降低资源消耗:重复利用已创建好的线程,减少线程的创建和销毁带来的资源消耗

2、提高系统的相应速度:任务到达时,不需要等待线程创建,直接利用现有线程可直接执行。

3、提升线程的可管理性:线程是稀缺资源,如果不加限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。使用线程池可以进行统一的分配和调优。

线程越多,不一定意味着程序越快。线程越多会造成上下文切换次数的增多,反而可能会拖慢整体的速度

02

线程池的构造函数的解释


public ThreadPoolExecutor(int corePoolSize,
                             
 int maximumPoolSize,
                             
 long keepAliveTime,
                              TimeUnit
 unit,
                              BlockingQueue<Runnable>
 workQueue,
                              ThreadFactory
 threadFactory,
                              RejectedExecutionHandler
 handler)

各个参数的作用:

  • corePoolSize :基本线程数(核心线程数),线程池的基本大小。当提交一个任务到线程池中时,线程池会创建一个线程来执行,即使当前线程池中其他的线程处于空闲状态。等到执行任务数大于基本线程数(corePoolSize)的时候便不再创建。线程池中提供了prestartAllCoreThreads 方法,调用此方法,线程池会提前创建并启动所有基本线程。

  • maximumPoolSize :最大线程数,线程池的最大大小。如果任务队列满了,并且已创建的线程数小于最大线程数,线程池则会创建新的线程执行任务。PS:当任务队列为无界队列是,此参数无效。

  • keepAliveTime :线程存活时间。线程池的工作线程处于空闲后存活的时间。任务很多,并且任务执行时间短的时候,可以增大时间,提升线程的利用率。

  • unit : 线程保持活动状态的时间单位。可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

  • workQueue :任务队列。用于保存等待任务执行的堵塞队列。可选的任务队列如下:

    • ArrayBlockingQueue:有界队列。基于数组的有界堵塞队列。FIFO

    • LinkedBlockingQueue:基于链表的堵塞队列。FIFO。吞吐量通常高于ArrayBlockingQueue。Executors.newFixedThreadPool()使用该队列。

    • SynchronousQueue :不存储元素的堵塞队列。Executors.newCachedThreadPool使用了这个队列。吞吐量通常高于LinkedBlockingQueue。

    • PriorityBlockingQueue:具有优先级的无限堵塞队列。

  • threadFactory:用于设置创建线程的工厂,可以通过线程工厂创建更有意义的名字。可以使用默认的工厂:Executors.defaultThreadFactory()

  • handler : 饱和策略。当线程和队列都满了的时候,线程池处于饱和状态,这时候需要提供一种策略处理新提交的任务。默认为AbortPolicy ,表示无法处理新任务的时候抛出异常。

    JDK提供了以下几种处理策略。

    • AbortPolicy:直接抛出异常。

    • CallerRunsPolicy:只用调用者所在线程来运行任务。

    • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。

    • DiscardPolicy:不处理,丢弃掉。

    • 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。

    如果使用ArrayBlockingQueue队列,那么线程池能接受的最大的任务数量为 maximumPoolSize+ArrayBlockingQueue堵塞队列长度。

03

使用例子


public class ThreadPoolDemo {

public static void main(String[] args) {
ThreadPoolExecutor
 pool = new ThreadPoolExecutor(10, 15, 100, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(5));

Monitor
 monitor = new Monitor(pool);
//启动监控器
new Thread(monitor).start();

// execute方法:只执行任务,无返回值
for (int i = 0; i < 10; i++) {
pool.execute(new RunnableTask());
}

// 关闭线程池,使用shutdown方法
System.
out.println("ShutDown ThreadPool....");
pool.shutdown();

try {
TimeUnit.
SECONDS.sleep(10);
}
 catch (InterruptedException e) {
e.printStackTrace();
}
//关闭监控
monitor.shutdown();
}

}


class RunnableTask implements Runnable{

@Override
public void run() {
System.
out.println("RunnableTask-" + Thread.currentThread().getName() +"-start");
try {
// 暂停10s
TimeUnit.
SECONDS.sleep(10);
}
 catch (InterruptedException e) {
e.printStackTrace();
}
System.
out.println("RunnableTask-" + Thread.currentThread().getName() +"-finished");
}

}


class RunnableTask implements Runnable{

@Override
public void run() {
System.
out.println("RunnableTask-" + Thread.currentThread().getName() +"-start");
try {
// 暂停10s
TimeUnit.
SECONDS.sleep(10);
}
 catch (InterruptedException e) {
e.printStackTrace();
}
System.
out.println("RunnableTask-" + Thread.currentThread().getName() +"-finished");
}

}



执行情况如下

[Monitor]isShutDown:false;ActiveCount:0;CompletedTaskCount:0;TaskCount:0;MaximumPoolSize:15;isTerminated:false
RunnableTask-pool-1-thread-1-start
RunnableTask-pool-1-thread-2-start
RunnableTask-pool-1-thread-3-start
RunnableTask-pool-1-thread-4-start
RunnableTask-pool-1-thread-5-start
RunnableTask-pool-1-thread-6-start
RunnableTask-pool-1-thread-7-start
RunnableTask-pool-1-thread-8-start
RunnableTask-pool-1-thread-9-start
ShutDown ThreadPool....
RunnableTask-pool-1-thread-10-start
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
[Monitor]isShutDown:true;ActiveCount:10;CompletedTaskCount:0;TaskCount:10;MaximumPoolSize:15;isTerminated:false
RunnableTask-pool-1-thread-3-finished
RunnableTask-pool-1-thread-4-finished
RunnableTask-pool-1-thread-5-finished
RunnableTask-pool-1-thread-1-finished
RunnableTask-pool-1-thread-2-finished
RunnableTask-pool-1-thread-7-finished
RunnableTask-pool-1-thread-10-finished
RunnableTask-pool-1-thread-9-finished
RunnableTask-pool-1-thread-8-finished
RunnableTask-pool-1-thread-6-finished

Monitor用来监控当前线程池的状态。包含当前的线程池是否关闭,活跃线程数,任务数等。


04

execute和submit区别

简单来说,execute和submit都是用来提交任务的。区别主要在于execute没有返回值而submit可以得到返回值。

在上面的代码基础上,我们添加如下内容,可以获取到返回值

main方法中,添加如下代码:

List<Future<String>> callList = new ArrayList<Future<String>>();
for (int i = 0; i < 10; i++) {
// submit方法,可以有返回值
Future<String>
 f = pool.submit(new Task());
callList.add(f);
}

//统一读取
for (Future<String> f : callList){
try {
String
 returnVal = f.get();//get()方法会堵塞。
System.
out.println("Task-" + returnVal +"-finished");
}
 catch (InterruptedException e) {
e.printStackTrace();
}
 catch (ExecutionException e) {
e.printStackTrace();
}
}


class Task implements Callable<String> {

@Override
public String call() throws Exception {
// 任务逻辑代码
System.
out.println("Task-" +Thread.currentThread().getName() +"-start" );
try {
// 暂停10s
TimeUnit.
SECONDS.sleep(10);
}
 catch (InterruptedException e) {
e.printStackTrace();
}
// 这里返回当前线程的Name
return Thread.currentThread().getName();
}

}


PS:注意一点,Future的get方法是堵塞的,会堵塞当前的线程直到有返回结果,所以,不要在提交任务(submit)的时候去使用Future的get方法,否则会导致任务变成顺序执行了,失去了多线程的意义。 

05

shutdown方法和shutdownNow的区别


这两个都是关闭线程池。

shutdown:首先将线程池状态是设置为SHUTDOWN状态。然后将线程池中空闲的线程interrupt掉,在shutdown之前提交的任务会继续执行完成。shutdown之后将不能再添加任务。上面有个任务的执行结果,如红字表示。调用shutdown之后,监控显示当前线程池已经处于shutdown状态了。但任务依旧在执行。但是isterminated处于false,说明任务还没有执行完成。 

shutdownNow:首先将线程池的状态设置为STOP,然后尝试中断所有正在执行或者暂停的线程。并返回等待执行任务的列表。 将线程池中所有的线程都interrupt掉。不关心当前线程是否正在执行任务。但并不意味着线程一定立马结束。如果任务无法响应中断,则无法被结束。 

只要调用了这两个方法中的一个方法,isShutdown都为true,当所有的任务都结束的时候,isTerminated为true,否则为false 


基本入门的知识点~线程池项目中二蛋用的比较少,最近也刚正儿八经的看,如果有错误,请指正~多谢~

版权所有:《攀爬蜗牛》 => 《ThreadPoolExecutor入门
本文地址:https://www.dutycode.com/threadpoolexecutor_rumen.html
除非注明,文章均为 《攀爬蜗牛》 原创,欢迎转载!转载请注明本文地址,谢谢。