Java线程池ExecutorService

1 篇文章 0 订阅
1 篇文章 0 订阅

转载于:
【Java线程】Java线程池ExecutorService
http://blog.csdn.net/vking_wang/article/details/9619137

线程池 ExecutorService 详细介绍以及注意点区别
http://blog.csdn.net/chenaini119/article/details/51849222

学习zookeeper时,看的代码中包含线程池,但是不知道怎么用,所以从网上转载,总结了两篇写的挺好的文章。

四种线程池

Java通过Executors提供四种线程池,分别为:

CachedThreadPool

CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来。会终止并且从缓存中移除已有60秒未被使用的线程。
如果线程有可用的,就使用之前创建好的线程,
如果线程没有可用的,就新创建线程。

重用:缓存型池子,先查看池中有没有以前建立的线程,如果有,就reuse;如果没有,就建一个新的线程加入池中

使用场景:缓存型池子通常用于执行一些生存期很短的异步型任务,因此在一些面向连接的daemon型SERVER中用得不多。

超时:能reuse的线程,必须是timeout IDLE内的池中线程,缺省timeout是60s,超过这个IDLE时长,线程实例将被终止及移出池。
结束:注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT不活动,其会自动被终止。

// 线程池的大小会根据执行的任务数动态分配  
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  

内部源代码,创建了一个线程数最小是0,最大是Integer.MAX_VALUE,空闲存活时间为60秒的线程池:

public static ExecutorService newCachedThreadPool() {  
    return new ThreadPoolExecutor(
        0,                 //core pool size  
        Integer.MAX_VALUE, //maximum pool size  
        60L,               //keep alive time  
        TimeUnit.SECONDS,  
       new SynchronousQueue<Runnable>());  
}  

FixedThreadPool

在FixedThreadPool中,有一个固定大小的池。
如果当前需要执行的任务超过池大小,那么多出的任务处于等待状态,直到有空闲下来的线程执行任务,
如果当前需要执行的任务小于池大小,空闲的线程也不会去销毁。

重用:fixedThreadPool与cacheThreadPool差不多,也是能reuse就用,但不能随时建新的线程

固定数目:其独特之处在于,任意时间点,最多只能有固定数目的活动线程存在,此时如果有新的线程要建立,只能放在另外的队列中等待,直到当前的线程中某个线程终止直接被移出池子

超时:和cacheThreadPool不同,FixedThreadPool没有IDLE机制(可能也有,但既然文档没提,肯定非常长,类似依赖上层的TCP或UDP IDLE机制之类的)

使用场景:所以FixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器

源码分析:从方法的源代码看,cache池和fixed 池调用的是同一个底层池,只不过参数不同:
fixed池线程数固定,并且是0秒IDLE(无IDLE)
cache池线程数支持0-Integer.MAX_VALUE(显然完全没考虑主机的资源承受能力),60秒IDLE

// 创建可以容纳3个线程的线程池  
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  

内部源代码,创建了一个线程数最小是3,最大是3,空闲存活时间为0秒的线程池:

public static ExecutorService newFixedThreadPool(int nThreads) {  
        return new ThreadPoolExecutor(
            nThreads,    //core pool size  
            nThreads,    //maximum pool size  
            0L,          //keep alive time  
            TimeUnit.MILLISECONDS,  
            new LinkedBlockingQueue<Runnable>());  
}  

SingleThreadExecutor

SingleThreadExecutor得到的是一个单个的线程,这个线程会保证你的任务执行完成。
如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。

// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务    
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();  

内部源代码,创建了一个线程数最小是1,最大是1,空闲存活时间为0秒的线程池:

public static ExecutorService newSingleThreadExecutor() {  
        return new FinalizableDelegatedExecutorService  
            (new ThreadPoolExecutor(
                1,  //core pool size  
                1,  //maximum pool size  
                0L, //keep alive time  
                TimeUnit.MILLISECONDS,  
                new LinkedBlockingQueue<Runnable>()));  
}  

ScheduledThreadPool

ScheduledThreadPool是一个固定大小的线程池,与FixedThreadPool类似,执行的任务是定时执行。

ScheduledThreadPool的有很多执行的方法,既可以即时执行,还可以定时执行,也可以循环定时执行。如下:

void execute(Runnable command)
即时执行

schedule(Runnable command, long delay, TimeUnit unit)
此函数的作用是,从现在开始的 delay 个单位之后执行command,单位是由unit定义,可以是秒,天等。

ScheduledFuture schedule(Callable callable,long delay, TimeUnit unit)
与上面的函数作用相同,只不过是一个传入的是Runnable接口的实现,另一个传入的是传入Callable的子类实现。

scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
该方法的作用是从现在开始的 initialDelay 个单位之后,每 period 个单位执行一遍 command,周期执行。
该方法延迟执行任务,设置任务的执行周期。时间周期从线程池中首先开始执行的线程算起,所以假设period为1s,线程执行了5s,那么下一个线程在第一个线程运行完后会很快被执行。

scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)
该方法的作用是从现在开始的 initialDelay 个单位之后,每 period 个单位执行一遍 command,周期执行。

上面两个方法的区别是,如果每 5 秒执行一次command,而 command要运行 2 秒才能执行完毕,假设两个函数都是在 1 秒 开始执行,
那么
scheduleAtFixedRate 的执行时间为,第一次 1s , 第二次 6s,第三次11s
scheduleWithFixedDelay 的执行时间为,第一次 1s,第二次 8s,第三次 15s。
即scheduleWithFixedDelay是执行完command之后才开始计时5s周期,而scheduleAtFixedRate 是开始执行command,就开始计时5s周期

// 效果类似于Timer定时器  
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  

内部源代码,创建了一个线程数最小是3,最大是3,空闲存活时间为0秒的线程池:  

public ScheduledThreadPoolExecutor(int corePoolSize) {  
        super(
              corePoolSize,      //core pool size  
              Integer.MAX_VALUE, //maximum pool size  
              0,                 //keep alive time  
              TimeUnit.NANOSECONDS,  
              new DelayedWorkQueue());  
}  

代码演示

private static void run(ExecutorService threadPool) {  
    for(int i = 1; i < 5; i++) {    
        final int taskID = i;    
        threadPool.execute(
            new Runnable() {    
            @Override  
            public void run() {    
                for(int i = 1; i < 5; i++) {    
                try {    
                     // 为了测试出效果,让每次任务执行都需要一定时间 
                    Thread.sleep(20);

                } catch (InterruptedException e) {    
                    e.printStackTrace();    
                }    
                System.out.println("第" + taskID + "次任务的第" + i + "次执行");    
                }    
            }    
        });    
    }    
        threadPool.shutdown();// 任务执行完毕,关闭线程池    
}  

public static void main(String[] args) {  

// 创建可以容纳3个线程的线程池  
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  

// 线程池的大小会根据执行的任务数动态分配  
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  

// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务    
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();  

// 效果类似于Timer定时器  
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  

        run(fixedThreadPool);  
//      run(cachedThreadPool);  
//      run(singleThreadPool);  
//      run(scheduledThreadPool);  
    }  

}  

CachedThreadPool执行结果:

4个任务是交替执行的。

第1次任务的第1次执行  
第4次任务的第1次执行  
第3次任务的第1次执行  
第2次任务的第1次执行  
第3次任务的第2次执行  
第4次任务的第2次执行  
第2次任务的第2次执行  
第1次任务的第2次执行  
第2次任务的第3次执行  
第4次任务的第3次执行  
第3次任务的第3次执行  
第1次任务的第3次执行  
第2次任务的第4次执行  
第1次任务的第4次执行  
第3次任务的第4次执行  
第4次任务的第4次执行  

FixedThreadPool执行结果

创建了一个固定大小的线程池,容量为3,然后循环执行了4个任务。由输出结果可以看到,前3个任务首先执行完,然后空闲下来的线程去执行第4个任务。

第1次任务的第1次执行  
第3次任务的第1次执行  
第2次任务的第1次执行  
第3次任务的第2次执行  
第2次任务的第2次执行  
第1次任务的第2次执行  
第3次任务的第3次执行  
第1次任务的第3次执行  
第2次任务的第3次执行  
第3次任务的第4次执行  
第1次任务的第4次执行  
第2次任务的第4次执行  
第4次任务的第1次执行  
第4次任务的第2次执行  
第4次任务的第3次执行  
第4次任务的第4次执行  

SingleThreadExecutor 执行结果

4个任务是顺序执行的。

第1次任务的第1次执行  
第1次任务的第2次执行  
第1次任务的第3次执行  
第1次任务的第4次执行  
第2次任务的第1次执行  
第2次任务的第2次执行  
第2次任务的第3次执行  
第2次任务的第4次执行  
第3次任务的第1次执行  
第3次任务的第2次执行  
第3次任务的第3次执行  
第3次任务的第4次执行  
第4次任务的第1次执行  
第4次任务的第2次执行  
第4次任务的第3次执行  
第4次任务的第4次执行  

ScheduledThreadPool执行结果

如果不使用定时执行和周期执行,那么scheduledThreadPool与FixedThreadPool没区别

第1次任务的第1次执行  
第2次任务的第1次执行  
第3次任务的第1次执行  
第2次任务的第2次执行  
第1次任务的第2次执行  
第3次任务的第2次执行  
第2次任务的第3次执行  
第1次任务的第3次执行  
第3次任务的第3次执行  
第2次任务的第4次执行  
第1次任务的第4次执行  
第3次任务的第4次执行  
第4次任务的第1次执行  
第4次任务的第2次执行  
第4次任务的第3次执行  
第4次任务的第4次执行 

细节

ExecutorService 的submit() 与execute()区别

  1. 接收的参数不一样
    submit()可以接受runnable无返回值和callable有返回值
    execute()接受runnable 无返回值
  2. submit有返回值,而execute没有
  3. submit方便Exception处理,
    Callable 的call方法申明为 call() throws Exception;
    Runnable的run方法申明为 run();
    即,Runnable的run方法是不能抛出异常的,而Callable的call方法可以抛出任何异常。
    意思就是如果你在你的task里会抛出checked或者unchecked exception,而你又希望外面的调用者能够感知这些exception并做出及时的处理,那么就需要用到submit,通过捕获Future.get抛出的异常。

shutdown() shutdownNow()区别

提供两个方法来关闭 ExecutorService。
1. shutdown() 方法在终止前允许执行以前提交的任务,
2. shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。在终止时执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。关闭未使用的 ExecutorService 以允许回收其资源。

一般分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务

Runnable()与Callable()区别

Runnable接口,不可以返回返回值,不可以抛出异常
Callable类,可以返回返回值,可以抛出任意异常。

参考文章:

java.util.concurrent.Future 类基础
http://www.oschina.net/translate/java-util-concurrent-future-basics

Java ScheduledThreadPoolExecutor延迟或周期性执行任务
http://blog.csdn.net/kuyuyingzi/article/details/17033973

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,可以使用配置类来配置Executor线程池。其中,常用的实现类是`ExecutorService`和`ThreadPoolTaskExecutor`。 首先,需要创建一个配置类,可以命名为`ThreadPoolConfig`或者其他你喜欢的名称。在这个类中,你需要使用`@Configuration`注解来标识它是一个配置类,并且使用`@EnableAsync`注解来启用异步执行。 接下来,你需要定义一个`ExecutorService`或`ThreadPoolTaskExecutor` Bean,并使用`@Bean`注解将其标识为一个Bean。你可以根据项目的需求来选择使用哪个实现类。 如果选择使用`ExecutorService`,可以按照以下方式配置: ```java @Configuration @EnableAsync public class ThreadPoolConfig { @Bean public ExecutorService executorService() { return Executors.newFixedThreadPool(10); // 配置线程池的大小 } } ``` 如果选择使用`ThreadPoolTaskExecutor`,可以按照以下方式配置: ```java @Configuration @EnableAsync public class ThreadPoolConfig { @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 设置核心线程数 executor.setMaxPoolSize(20); // 设置最大线程数 executor.setQueueCapacity(30); // 设置队列容量 executor.setThreadNamePrefix("my-executor-"); // 设置线程名称前缀 executor.initialize(); // 初始化 return executor; } } ``` 在上述配置中,你可以根据实际需求来设置线程池的大小、队列容量等参数。通过这种方式,你就可以在应用程序中注入`ExecutorService`或`ThreadPoolTaskExecutor`,并使用它来执行异步任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值