文章目录
为什么使用线程池
- 降低资源消耗,不用反复创建
- 提高相应速度,不用立即创建
- 提高线程管理性,进行统一分配,调度
主要特点
- 线程复用
- 控制最大并发数
- 管理线程
类似spring的生命周期
java中的string常量池
线程池如何使用
Java中的线程池通过Executor框架实现的,该框架用到了Executor、Executors(辅助工具类)、ExecutorService、ThreadPoolExecutor
newFixedThreadPool
创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
newCachedThreadPool
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
newScheduleThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
其实底层都是ThreadPoolExecutor,只是参数不同和阻塞队列不同导致不同的使用方法。
线程池的7大重要参数
corePoolSize
核心线程数
maximumPoolSize
最大线程数,一般大于等于核心线程数
workQueue
任务队列
keepAliveTime
线程存活时间
unit
存活时间单位
threadFactory
创建线程的工程
handler
拒绝策略
拒绝策略
- AbortPolicy:直接抛异常(默认)
- DiscardOldestPolicy:丢弃最前面的任务,然后重新尝此次任务(重复)
- DiscardPolicy:丢弃任务,不抛异常
- CallerRunsPolicy:优调度线程处理此任务,回到调用者去处理
线程池底层工作原理
- 创建线程池,等待提交过来的任务请求;
- 调用execute()方法添加一个请求任务时,会有一下逻辑
- 如果正在运行的线程数小于core,立马创建
- 如果大于core,将任务放入队列
- 如果队列满了,且小于max,创建非核心线程数立即运行任务
- 如果队列满了,且超过最大max,执行拒绝策略
- 一个线程完成任务,会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过keepAliveTime,线程池会判断,如果当前运行的线程数大于core,这个线程会被停掉。所有线程最终完成任务时,会收缩到core的大小。
工作中你用到哪个?
java提供的都不要用,用自定义参数的线程池!!
阿里巴巴开发手册规定:
fixed和single 会创建Integer.MAX_VALUE的队列 oom
cached和schedule会创建Integer.MAX_VALUE的线程 oom
手写线程池
后续结合配置中心 根据数据量 做线程池的动态参数调整
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
try {
for (int i = 1; i <= 9; i++) {
threadPoolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "办理业务");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
pool-1-thread-1办理业务
pool-1-thread-4办理业务
main办理业务
pool-1-thread-3办理业务
pool-1-thread-2办理业务
pool-1-thread-3办理业务
pool-1-thread-4办理业务
pool-1-thread-5办理业务
pool-1-thread-1办理业务
线程池配置合理参数
CPU密集型
IO密集型
线程数=CPU核数/1=(0.8~0.9)
死锁编码及定位分析
死锁原因
- 俩个以上的线程,抢夺资源互相等待的一种现象。
死锁代码以及分析
package com.example.thread;
class HoldLocKThread implements Runnable{
private String lockA;
private String lockB;
public HoldLocKThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+":自己有"+lockA+"想获得"+lockB);
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+":自己有"+lockB+"想获得"+lockA);
}
}
}
}
/**
* @author qiumeng
* @version 1.0
* @description
* @date 2020/8/23 10:46
*/
public class DeadLocKDemo {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new HoldLocKThread(lockA,lockB),"aaaaa").start();
new Thread(new HoldLocKThread(lockB,lockA),"bbbbbb").start();
}
}
解决方案
- jsp -l
10784 com.example.thread.DeadLocKDemo
11312
9696 org.jetbrains.jps.cmdline.Launcher
8248 sun.tools.jps.Jps
3724 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
9596 org.jetbrains.idea.maven.server.RemoteMavenServer
- jstack 进程id
"bbbbbb":
at com.example.thread.HoldLocKThread.run(DeadLocKDemo.java:20)
- waiting to lock <0x000000076b145340> (a java.lang.String)
- locked <0x000000076b145378> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"aaaaa":
at com.example.thread.HoldLocKThread.run(DeadLocKDemo.java:20)
- waiting to lock <0x000000076b145378> (a java.lang.String)
- locked <0x000000076b145340> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.