—- 原文有参看其他博主博客 —
在Java中提供了Executors类可以帮助我们快速构建一些线程池,下面是4种线程池的基本使用例子
-
MyTaskThread 类
/** * MyTaskThread类,实现Runnable接口 * @Author fx * @date 2019/08/16 */ public class MyTaskThread implements Runnable { @Override public void run() { // 这里写你的业务逻辑 // ... System.out.println(Thread.currentThread().getName() + "线程执行了"); } }
1.newFixThreadPool – 固定线程池
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- corePoolSize与maximumPoolSize相等,即其线程全为核心线程
- keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
- workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值为Integer.MAX_VALUE,很可能造成OOM(内存溢出)
构造方法:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
* 此方法用于创建固定的线程池
*/
public static void fixedPool(){
// 创建固定数量的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 创建线程
Runnable r1 = new MyTaskThread();
Runnable r2 = new MyTaskThread();
Runnable r3 = new MyTaskThread();
// 将线程放入线程池中执行
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
// 待任务执行完成,关闭线程池
pool.shutdown();
}
执行结果:
pool-1-thread-2线程执行了
pool-1-thread-3线程执行了
pool-1-thread-1线程执行了
进程已结束,退出代码0
可以看出,放进线程池的3个线程都被执行了
2.newSingleThreadExecutor – 单一线程池
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- corePoolSize与maximumPoolSize参数都为1,表示只创建一个线程
- keepAliveTime = 0 该参数默认对核心线程无效
- 被FinalizableDelegatedExecutorService包装,表明SingleThreadExecutor被定以后,无法修改,做到了真正的Single
构造方法:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* 此方法用于创建单一线程池
* 每次调用execute方法,其实最后都是调用了thread-1的run方法。
*/
public static void singleThreadPool(){
//创建单一线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
// 创建线程
Runnable r1 = new MyTaskThread();
Runnable r2 = new MyTaskThread();
Runnable r3 = new MyTaskThread();
// 将线程放入线程池中执行
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
// 待任务执行完成,关闭线程池
pool.shutdown();
}
执行结果:
pool-1-thread-1线程执行了
pool-1-thread-1线程执行了
pool-1-thread-1线程执行了
进程已结束,退出代码0
虽然这里创建了3个线程,然而线程池里执行的其实还是thread1的run方法
3.newCachedThreadPool – 缓存线程池
创建一个可缓存的线程池,可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,当任务数增加时,此线程池又可以
智能
的添加新线程来处理任务。此线程池不会对线程池大小做限制
- corePoolSize = 0,maximumPoolSize = Integer.MAX_VALUE表明线程创建几乎无限制
- keepAliveTime = 60s,线程空闲60s后自动结束
- workQueue 为 SynchronousQueue 队列,表明有任务就创建线程,来着不拒
构造方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* 此方法用于创建缓存线程池
* 这种方式的特点是:可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们
*/
public static void cachedPool(){
ExecutorService pool = Executors.newCachedThreadPool();
// 创建线程
Runnable r1 = new MyTaskThread();
Runnable r2 = new MyTaskThread();
Runnable r3 = new MyTaskThread();
Runnable r4 = new MyTaskThread();
// 将线程放入线程池中执行
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
pool.execute(r4);
// 待任务执行完成,关闭线程池
pool.shutdown();
}
执行结果:
pool-1-thread-1线程执行了
pool-1-thread-4线程执行了
pool-1-thread-3线程执行了
pool-1-thread-2线程执行了
进程已结束,退出代码0
4.newScheduledThreadPool – 支持周期任务的线程池
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求
构造方法:
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
/**
* 此方法用于创建支持定时任务的线程池
*/
public static void schedulePool(){
// 创建支持周期运行的线程池
ScheduledExecutorService pool = Executors.newScheduledThreadPool(4);
// 创建线程
Runnable r1 = new MyTaskThread();
Runnable r2 = new MyTaskThread();
// 将线程放入线程池中执行
pool.execute(r1);
// 带有延迟时间的执行,1秒后执行
pool.schedule(r2,1000,TimeUnit.MILLISECONDS);
// 待任务执行完成,关闭线程池
pool.shutdown();
}
执行结果
pool-1-thread-1线程执行了
// 这里中间间隔了1秒
pool-1-thread-2线程执行了
进程已结束,退出代码0
ThreadPoolExecutor类的使用探讨
在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。
下面是自定义线程池使用例子:
public static void main(String[] args) {
// 核心线程池数量
int corePoolSize = 2;
// 最大线程池数量
int maximumPoolSize = 4;
// 空闲等待时间
long keepAliveTime = 10;
// 时间单位,秒
TimeUnit unit = TimeUnit.SECONDS;
// 有界阻塞队列
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
// 创建线程的工厂,这里我们自定义一个自己的实现
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger mThreadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
};
// 拒绝策略,丢弃任务并抛出RejectedExecutionException
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
// 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
// 预启动所有核心线程
executor.prestartAllCoreThreads();
// 创建线程
for(int i=1;i<=10;i++){
MyTaskThread taskThread = new MyTaskThread();
executor.execute(taskThread);
}
try {
// 阻塞主线程
System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
}
执行结果:
my-thread-1 has been created
my-thread-2 has been created
my-thread-3 has been created
my-thread-1线程执行了
my-thread-2线程执行了
my-thread-2线程执行了
my-thread-4 has been created
my-thread-1线程执行了
my-thread-3线程执行了
my-thread-3线程执行了
my-thread-2线程执行了
my-thread-1线程执行了
my-thread-4线程执行了
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.aynu.fx.test.multithread.MyTaskThread@4d405ef7 rejected from java.util.concurrent.ThreadPoolExecutor@6193b845[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 9]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.aynu.fx.test.multithread.MultiThreadTest.main(MultiThreadTest.java:48)
通过自定义线程池,我们可以更好的让线程池为我们所用,更加适应我的实际场景.