首先,让我们问一个问题,为什么要使用线程池?很多时候,我们使用多线程来执行任务的时候都是这样一个场景:每新建一个任务就对应地创建一个线程,而往往每个任务在很短的时间内就结束了,导致线程的创建和销毁频繁发生,这会降低系统的效率。线程池的应用便应运而生。
一,Java线程池
1,ThreadPoolExecutor
ThreadPoolExecutor是线程池任务执行器。其继承链为
ThreadPoolExecutor extends AbstractExecutorService implements ExecutorServiceextendsExecutor
ThreadPoolExecutor的几个重要构造参数包括以下:
1)corePoolSize:核心池尺寸,当池大小小于corePoolSize时,就新建线程来执行任务。当池大小等于corePoolSize时,就将任务放进workQueue阻塞队列。池子中的空闲线程去队列中获取任务来执行。
2)maximumPoolSize:最大池尺寸,当队列已经满了,就新建线程入池来处理请求,但是线程数量不能超过最大池尺寸。
3)keepAliveTime:线程存活时间,决定当一个线程空闲了多长时间后被销毁。只有在线程池尺寸大于corePoolSize时才起作用。
4)unit:时间单位,有7种选择:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
5)workQueue:阻塞队列,用来存储待执行的任务。声明类型为BlockingQueue
ArrayBlockingQueue |
基于数组,先进先出,必须指定大小 |
LinkedBlockingQueue |
基于链表,先进先出,如未指定大小,则为Integer.MAX_VALUE |
SynchronousQueue |
没有尺寸的概念,不会保存提交的任务,而是将直接新建一个线程来执行新来的任务 |
PriorityBlockingQueue |
其存储的对象必须实现Comparable接口,队列根据compare接口方法确定任务的优先级 |
常用的是LinkedBlockingQueue和SynchronousQueue
6)threadFactory,线程工厂,用来创建线程。
7)handler,拒绝处理任务时的策略,包括
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
ThreadPoolExecutor的几个重要方法
1)execute()
向线程池提交任务,交由线程池来执行
2)submit()
同样是向线程池提交任务,其内部调用了execute(),区别是,会返回一个FutureTask实例
3)shutdown()
等任务都执行完毕后关闭线程池,并且拒绝接受新的任务
4)shutdownNow()
停止正在执行的任务,立刻关闭线程池,拒绝接受新的任务
5)isTerminated()
检查线程池是否已关闭
代码示例:
任务类
package com.wxy.popcorn.threadpool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Task implements Runnable {
private Logger logger = LoggerFactory.getLogger(getClass());
private String tName;
public Task(String tName) {
this.tName = tName;
}
public void run() {
logger.info("{} is executing the {}", Thread.currentThread().getName(), tName);
}
}
任务执行器
package com.wxy.popcorn.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TaskExecutor {
private Logger logger = LoggerFactory.getLogger(getClass());
private int executeTimes;
private ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 10, 2000L,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue(100), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
public TaskExecutor(int executeTimes) {
this.executeTimes = executeTimes;
}
public void executeTasks() {
for (int i=0; i< executeTimes; i++) {
threadPoolExecutor.execute(new Task("task"+i));
}
threadPoolExecutor.shutdown();
while (true) {
if (threadPoolExecutor.isTerminated()) {
logger.info("==========End to execute the tasks==========");
break;
}
}
}
}
测试类
package com.wxy.popcorn.threadpool;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TaskExecutorTest {
private Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void test() {
logger.info("==========Begin to execute tasks==========");
new TaskExecutor(20).executeTasks();
}
}
测试结果
2017-12-25 23:15:28.735 [main] INFO [TaskExecutorTest.java:12] ==========Begin to execute tasks==========
2017-12-25 23:15:28.753 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task2
2017-12-25 23:15:28.753 [pool-2-thread-1] INFO [Task.java:15] pool-2-thread-1 is executing the task0
2017-12-25 23:15:28.753 [pool-2-thread-2] INFO [Task.java:15] pool-2-thread-2 is executing the task1
2017-12-25 23:15:28.756 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task4
2017-12-25 23:15:28.756 [pool-2-thread-1] INFO [Task.java:15] pool-2-thread-1 is executing the task5
2017-12-25 23:15:28.756 [pool-2-thread-4] INFO [Task.java:15] pool-2-thread-4 is executing the task3
2017-12-25 23:15:28.756 [pool-2-thread-2] INFO [Task.java:15] pool-2-thread-2 is executing the task6
2017-12-25 23:15:28.756 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task7
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO [Task.java:15] pool-2-thread-1 is executing the task8
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO [Task.java:15] pool-2-thread-4 is executing the task9
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO [Task.java:15] pool-2-thread-2 is executing the task10
2017-12-25 23:15:28.757 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task11
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO [Task.java:15] pool-2-thread-1 is executing the task12
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO [Task.java:15] pool-2-thread-4 is executing the task13
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO [Task.java:15] pool-2-thread-2 is executing the task14
2017-12-25 23:15:28.757 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task15
2017-12-25 23:15:28.757 [pool-2-thread-1] INFO [Task.java:15] pool-2-thread-1 is executing the task16
2017-12-25 23:15:28.757 [pool-2-thread-4] INFO [Task.java:15] pool-2-thread-4 is executing the task17
2017-12-25 23:15:28.757 [pool-2-thread-2] INFO [Task.java:15] pool-2-thread-2 is executing the task18
2017-12-25 23:15:28.758 [pool-2-thread-3] INFO [Task.java:15] pool-2-thread-3 is executing the task19
2017-12-25 23:15:28.758 [main] INFO [TaskExecutor.java:28] ==========End to execute the tasks==========
2,其它Java线程池
除了ThreadPoolExecutor,jdk还提供了其它多种线程池。通过Executors类的成员方法来生成实例。
1)newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
2)newFixedThreadPool
public static ExecutorService newFixedThreadPool(intnThreads){
return new ThreadPoolExecutor(nThreads,nThreads,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
3)newScheduledThreadPool,创建一个无尺寸上限的线程池,支持定时和周期性任务。
public static ScheduledExecutorService newScheduledThreadPool(intcorePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
4)newSingleThreadExecutor,只会使用唯一的线程来执行任务,保证所有的任务都按照指定顺序执行。
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1,1,
0L,TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
二,spring线程池
ThreadPoolTaskExecutor是spring提供的线程池。其使用与Java线程池类似,不做赘述。直接查看示例代码。这里使用spring-boot的方式来进行配置。
任务类:
package com.wxy.popcorn.springthreadpool;
import java.util.concurrent.CountDownLatch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpringTask implements Runnable{
private Logger logger = LoggerFactory.getLogger(getClass());
private String tName;
private CountDownLatch endGate;
public SpringTask(String tName) {
this.tName = tName;
}
public void run() {
logger.info("{} is executing the {}", Thread.currentThread().getName(), tName);
}
}
配置类
package com.wxy.popcorn.springthreadpool;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@PropertySource("classpath:threadpool.properties")
public class SpringConfig {
@Bean
@ConfigurationProperties(prefix = "thread.pool")
public ThreadPoolTaskExecutor taskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
属性文件
#线程池
#线程池维护线程的最少数量
thread.pool.core-pool-size=10
#线程池维护线程的最大数量
thread.pool.max-pool-size=20
#缓存队列
thread.pool.queue-capacity=100
#允许的空闲时间
thread.pool.keep-alive-seconds=300
启动类
package com.wxy.popcorn.springthreadpool;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
SpringApplication.run(SpringApp.class, args);
}
}
测试类
package com.wxy.popcorn.springthreadpool;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringTaskExecutorTest {
@Autowired
ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Test
public void test() {
for (int i=0; i< 20; i++) {
threadPoolTaskExecutor.execute(new SpringTask("task"+i));
}
}
}
测试结果
2017-12-25 23:08:59.215 [taskExecutor-2] INFO [SpringTask.java:18] taskExecutor-2 is executing the task1
2017-12-25 23:08:59.215 [taskExecutor-5] INFO [SpringTask.java:18] taskExecutor-5 is executing the task4
2017-12-25 23:08:59.215 [taskExecutor-9] INFO [SpringTask.java:18] taskExecutor-9 is executing the task8
2017-12-25 23:08:59.216 [taskExecutor-4] INFO [SpringTask.java:18] taskExecutor-4 is executing the task3
2017-12-25 23:08:59.216 [taskExecutor-3] INFO [SpringTask.java:18] taskExecutor-3 is executing the task2
2017-12-25 23:08:59.215 [taskExecutor-1] INFO [SpringTask.java:18] taskExecutor-1 is executing the task0
2017-12-25 23:08:59.215 [taskExecutor-7] INFO [SpringTask.java:18] taskExecutor-7 is executing the task6
2017-12-25 23:08:59.215 [taskExecutor-6] INFO [SpringTask.java:18] taskExecutor-6 is executing the task5
2017-12-25 23:08:59.215 [taskExecutor-8] INFO [SpringTask.java:18] taskExecutor-8 is executing the task7
2017-12-25 23:08:59.216 [taskExecutor-10] INFO [SpringTask.java:18] taskExecutor-10 is executing the task9
2017-12-25 23:08:59.224 [taskExecutor-2] INFO [SpringTask.java:18] taskExecutor-2 is executing the task10
2017-12-25 23:08:59.224 [taskExecutor-5] INFO [SpringTask.java:18] taskExecutor-5 is executing the task11
2017-12-25 23:08:59.224 [taskExecutor-9] INFO [SpringTask.java:18] taskExecutor-9 is executing the task12
2017-12-25 23:08:59.224 [taskExecutor-4] INFO [SpringTask.java:18] taskExecutor-4 is executing the task13
2017-12-25 23:08:59.224 [taskExecutor-3] INFO [SpringTask.java:18] taskExecutor-3 is executing the task14
2017-12-25 23:08:59.224 [taskExecutor-1] INFO [SpringTask.java:18] taskExecutor-1 is executing the task15
2017-12-25 23:08:59.225 [taskExecutor-7] INFO [SpringTask.java:18] taskExecutor-7 is executing the task16
2017-12-25 23:08:59.225 [taskExecutor-6] INFO [SpringTask.java:18] taskExecutor-6 is executing the task17
2017-12-25 23:08:59.225 [taskExecutor-8] INFO [SpringTask.java:18] taskExecutor-8 is executing the task18
2017-12-25 23:08:59.225 [taskExecutor-10] INFO [SpringTask.java:18] taskExecutor-10 is executing the task19