线程池用过吗?ThreadPoolExecutor谈谈你的理解
作者:互联网
线程池的主要工作是控制运行的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了池最大数量,超出的线程排队等候,等其他线程执行完毕,再从队列取出任务来执行。
它的主要特点为:线程复用,控制最大并发数,管理线程
为什么要用线程池,优势:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一分配,调优和监控
线程池如何使用:
ExecutorService threadPool = Executors.newFixedThreadPool(3);
固定线程数的线程池。执行长期的任务,性能好很多ExecutorService threadPool = Executors.newSingleThreadExecutor();
单一线程的线程池。一个任务一个任务执行的场景ExecutorService threadPool = Executors.newCachedThreadPool();
可扩容可回收的线程池。执行很多短期异步的小程序或者负载较轻的服务器
线程池的几个重要参数介绍:
- corePoolSize:线程池中的常驻核心参数
- maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值必须大于1
- keepAliveTime:多余的空闲线程存活时间。当前线程池中线程数量超过corePoolSize值时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个为止
- unit:keepAliveTime的单位
- workQueue:任务队列,被提交但尚未执行的任务
- threadFactory:表示生成线程池中工作线程的线程工厂,用于创建线程,一般用默认的即可
- handler:拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数maximumPoolSize
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
说说线程池的底层工作原理:
- 创建线程池后,等待提交过来的任务请求
- 当调用execute()方法添加一个请求任务时,线程会作如下判断
2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务
2.2 如果正在运行的线程数量大于或等于corePoolSize,那么这个任务将进入等待队列
2.3 如果这时队列满了且正在运行的线程数量还是小于maximumPoolSize,那么还是要创建非核心线程立即运行这个任务
2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行 - 当一个线程完成任务时,他会从队列中取下一个任务来执行
- 当一个线程无事可做超过一定的时间keepAliveTime时,线程池会判断:
如果当前运行的线程数量超过corePoolSize,那么这个线程就会被停掉
所以线程池的所有任务完成后最终会收缩到corePoolSize的大小
第三种获得多线程的方法:
Callable 接口
如何使用Callable创建一个线程: 通过FutureTask的构造方法
模拟做菜准备:买厨具、买菜。厨具是网购,买菜是自己去
public static void main(String[] args) {
long start = System.currentTimeMillis();
Thread tool = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 购买厨具");
System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " 厨具送到");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "toolThread");
tool.start();
//模拟等待送货时间,因为必须要得到厨具才能做饭,而送货时间是不确定的,送货线程也没有返回值,我们无法知道该什么时候去获取厨具,所以一直等他的结果
try {
tool.join();
System.out.println("获得厨具:"+ "厨具");
System.out.println("shopThread开始去买菜");
//买菜时间
TimeUnit.SECONDS.sleep(3);
System.out.println("shopThread买菜完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("准备工作完毕");
long end = System.currentTimeMillis();
System.out.println("共用时:" + (end-start) / 1000 + "s");
}
toolThread 购买厨具
toolThread 厨具开始送货
toolThread 厨具送到
获得厨具:厨具
shopThread开始去买菜
shopThread买菜完毕
准备工作完毕
共用时:6s
这两个过程应该可以同时分开进行
public static void main(String[] args) {
long start = System.currentTimeMillis();
FutureTask<String> toolTask = new FutureTask(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 购买厨具");
System.out.println(Thread.currentThread().getName() + " 厨具开始送货");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " 厨具送到");
return "厨具";
} catch (InterruptedException e) {
throw new InterruptedException();
}
});
new Thread(toolTask, "toolThread").start();
try {
System.out.println("shopThread开始去买菜");
//买菜时间
TimeUnit.SECONDS.sleep(3);
System.out.println("shopThread买菜完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
String tool = toolTask.get(); //获取异步线程工作的返回值,如果还没执行完会造成阻塞,所以尽量放在最后
//可以用 toolTask.isDOne() 判断是否执行完成
System.out.println("获得厨具:" + tool);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("准备工作完毕");
long end = System.currentTimeMillis();
System.out.println("共用时:" + (end-start) / 1000 + "s");
}
shopThread开始去买菜
toolThread 购买厨具
toolThread 厨具开始送货
shopThread买菜完毕
toolThread 厨具送到
获得厨具:厨具
准备工作完毕
共用时:3s
拥有相同FutureTask的线程启动,只会运行一次,它的结果可以复用
new Thread(toolTask, "AA").start();
new Thread(toolTask, "BB").start();
第四种获得多线程的方法,线程池:
Java中的线程池是通过Executor框架实现的,该框架中用到了Executor, Executors. ExecutorService, ThreadPoolExecutor 这几个类
编码实现:
- (了解)Executoes.newScheduledThreadPool()
- (了解 java8新出)Executors.newWorkStealingPool(int) 使用目前机器上可用的处理器作为他的并行级别
ExecutorService executorService = Executors.newFixedThreadPool(3);
固定线程数的线程池。执行长期的任务,性能好很多ExecutorService executorService = Executors.newSingleThreadExecutor();
单一线程的线程池。一个任务一个任务执行的场景ExecutorService executorService = Executors.newCachedThreadPool();
可扩容可回收的线程池。执行很多短期异步的小程序或者负载较轻的服务器
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try {
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
for (int i = 0; i < 6 ; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " doing job");
try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
try {
for (int i = 0; i < 6 ; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " doing job");
try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
pool-1-thread-1 doing job
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 6 ; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " doing job");
try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
pool-1-thread-1 doing job
pool-1-thread-2 doing job
pool-1-thread-3 doing job
pool-1-thread-4 doing job
pool-1-thread-5 doing job
pool-1-thread-6 doing job
这三个线程池构建方法的底层: ThreadPoolExecutor
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {}
标签:doing,池用,System,厨具,job,线程,println,ThreadPoolExecutor 来源: https://blog.csdn.net/sakuragio/article/details/100656399