Day289.线程池 -Juc
Posted 阿昌喜欢吃黄桃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day289.线程池 -Juc相关的知识,希望对你有一定的参考价值。
线程池
一、线程池的自我介绍
1、线程池的重要性
- 复用每一个线程
- 控制资源总量,便于管理
2、什么是 “池”
- 软件中的
“池”
,可以理解为计划经济
3、不使用线程池会怎么样
-
每次创建和销毁线程都需要资源的
开销
,会有很大部分的资源浪费
-
一个线程
public class EveryTaskOneThread {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.start();
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println("执行了任务");
}
}
}
- for循环创建10个线程执行任务
public class ForLoop {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Task());
thread.start();
}
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println("执行了任务");
}
}
}
- 当任务数量上升到1000个数量级
*这种for循环1000的情况,java语言中的线程会直接对应操作系统中的1000个线程,也就是说在操作系统中创建了1000个线程,带来巨大的开销
===>内存、垃圾回收器等…
4、为什么需要使用线程池
-
反复创建线程开销大
-
过多的线程会导致内存消耗
5、线程池的好处
- 加快响应速度
- 便于线程的管理控制,方便数据统计
- 合理利用CPU和内存
6、线程池适合的场合
二、创建和停止线程池
1、线程池构造函数的参数
- 参数:corePoolSize:
线程池初始化后,默认是没有任何线程的,线程池会等待有任务到来时,再去创建新线程去执行任务;
核心的数量就是保持活跃的线程数量
- 参数:maxPoolSize:
线程创建上限
- 增加线程规则:
- 流程图:
- 是否需要增加线程的判断:
- corePoolSzie
- workQueue
- maxPoolSize
- 举一个例子:
-
增减线程的特点:
-
通过设定
corePoolSize & maxPoolSize相同
,可以创建固定大小的线程池
-
线程池希望
保持较少的线程数量
,并且只有在负载变得很大时才去增加它 -
通过设定maxPoolSize的值很高,如Integer.MAX_VALUE,可以创建出一个
容纳任意数量
的并发任务 -
如果设置一个
无上限的队列
(如LinkedBlockingQueue),那么线程数就不会超过corePoolSize
-
- 参数:keepAliveTime:
如果线程池当前的线程数多于corePoolSize,那么如果多余的线程空闲时间超过keepAliveTime,多次来超时的就会被回收终止
- 参数:ThreadFactory
用来创建线程
- 参数:workQueue:
2、线程池选择 [手动创建]&[自动创建]?
手动创建线程池更好,可以明确线程池运行规则
,避免资源耗尽风险
①newFixedThreadPool
- 源码:
- 代码演示:
/******
@author 阿昌
@create 2021-06-05 18:53
*******
* 演示newFixedThreadPool
*/
public class FixedThreadPoolTest {
//主函数
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//执行线程池
for (int i = 0; i < 1000; i++) {
threadPool.execute(new Task());
}
}
//任务类
static class Task implements Runnable {
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
↓↓↓,发现只有5个线程在执行任务,跟我们设定5个线程执行要求是一致的
- 演示错误
当因为任务过多无法处理导致内存资源耗尽*
/******
@author 阿昌
@create 2021-06-05 19:03
*******
* 演示newFixedThreadPool出错的情况
*/
public class FixedThreadPoolOOM {
//线程池
private static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
//执行任务
executorService.execute(new SubThread());
}
}
}
//任务类
class SubThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
一直执行,观察任务管理器,发现内存直接大量增加
发现太慢了,我们就直接主动设置jvm参数让他快点出现内存溢出的情况
-Xmx8m -Xms8m
最后出现了内存溢出
②newSingleThreadExecutor
- 代码演示
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++) {
executorService.execute(new Task());
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 源码:
③newCachedThreadPool
可缓存线程池
-
特点:采用SynchronousQueue
无界限线程池
,具有60s自动回收多余线程
的功能 -
源码:
- 代码演示
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
pool.execute(new Task());
}
}
static class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
每一个任务,他就创建一个线程,↓↓↓他创建了1000个线程
④newScheduledThreadPool
-
支持定时及周期性任务执行的线程池
-
代码演示
public class ScheduleThreadPool {
public static void main(String[] args) {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
//5秒钟执行任务,不重复执行
// pool.schedule(new Task(),5, TimeUnit.SECONDS);
//一开始1后执行任务,后面每隔3秒执行
pool.scheduleAtFixedRate(new Task(),1,3,TimeUnit.SECONDS);
}
}
3、线程池设定线程数量如何选择?
4、停止线程的正确方法
-
shutdown
-
isShutdown
-
isTerminated
-
awaitTermnation
-
shutdownNow
代码演示:
//演示关闭线程池
public class ShutdownPool {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
pool.execute(new ShutdownTask());
}
Thread.sleep(1500);
pool.shutdownNow();
// boolean flag = pool.awaitTermination(3, TimeUnit.SECONDS);
// System.out.println(flag);
// System.out.println(pool.isShutdown());
//关闭线程池
// pool.shutdown();
// System.out.println(pool.isShutdown());
// Thread.sleep(10000);
// System.out.println(pool.isTerminated());
//再次执行任务
// pool.execute(new ShutdownTask());
}
}
class ShutdownTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+": 被中断了");
}
}
}
三、常见线程池的特点和用法
四、任务太多,怎么拒绝?
1、拒绝时机
2、拒绝策略
3、钩子方法
//演示任务前后,可有钩子桉树
public class PauseableThreadPool extends ThreadPoolExecutor {
private boolean isPaused;//标记位
private final ReentrantLock lock = new ReentrantLock();//锁
private final Condition unpaused = lock.newCondition();//类似wait/notify...
//主函数
public static void main(String[] args) throws InterruptedException {
PauseableThreadPool pool = new PauseableThreadPool(10, 20, 10L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("我被执行了");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < 10000; i++) {
pool.execute(task);
}
Thread.sleep(1500);
pool.pause();
System.out.println("线程池被暂停了。。。。。。。。。。");
Thread.sleep(1500);
pool.resume();
System.out.println("线程池恢复了。。。。。。。。。。。。");
}
//钩子函数,执行前
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
lock.lock();
try {
while (isPaused) {
//暂停线程
unpaused.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
//暂停
private void pause() {
lock.lock();
try {
isPaused = true;
} finally {
lock.unlock();
}
}
//恢复
private void resume() {
lock.lock();
try {
isPaused = false;
//唤醒线程
unpaused.signalAll();
} finally {
lock.unlock();
}
}
//下面↓↓↓ 是继承 ThreadPoolExecutor 默认实现的
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
}
五、线程池实现原理
1、线程池组成部分
-
线程池管理器
-
工作线程
-
任务队列
-
任务接口(Task)
2、Executor家族
- 哪个是线程池
- Executor
-
ExecutorService
-
Executors: 工具类
3、线程池实现任务复用
在start()里面执行各个任务的run()方法
在while中不能的循环执行
六、线程池状态
- execute()方法的源码
七、使用线程池的注意点
以上是关于Day289.线程池 -Juc的主要内容,如果未能解决你的问题,请参考以下文章