- ThreadPoolExecutor 类
//java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类
//ThreadPoolExecutor继承了AbstractExecutorService
//AbstractExecutorService是一个抽象类,它实现了ExecutorService接口
//ExecutorService又是继承了Executor接口
//Executor是一个顶层接口,里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
// 参数说明:
1、corePoolSize:核心池的大小
2、maximumPoolSize:线程池最大线程数,表示在线程池中最多能创建多少个线程;
3、keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
4、unit:参数keepAliveTime的时间单位
5、workQueue:一个阻塞队列,用来存储等待执行的任务
- 创建线程池
- ThreadPoolExecutor:直接通过ThreadPoolExecutor的构造方法创建
- Executors:使用Executors工具类创建
注:《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
// 1
//直接用 ThreadPoolExecutor 类创建线程池
//注:不提倡直接使用ThreadPoolExecutor 创建线程池,因为手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数量来进行配置。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5,
10,
200,
TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5));
// 2
//推荐使用Executors类中提供的几个静态方法来创建线程池,例如:
ExecutorService executor = Executors.newFixedThreadPool(5);
//其实他们的底层还是使用ThreadPoolExecutor 类来创建线程池,只不过是使用了默认参数
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
1、newFixedThreadPool(): 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。但线程空闲时不会被释放,一致占用系统内存资源
2、newCachedThreadPool():创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
3、newScheduledThreadPool(): 创建一个定长任务线程池,支持定时及周期性任务执行。
4、newSingleThreadExecutor(): 创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
- 线程池使用
public static void main(String[] args) {
ThreadLocal<String> thradLocalName = new ThreadLocal<>();
thradLocalName.set("小明");
// 创建一个线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 执行任务
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("...");
}
});
}
- 线程池5种运行状态
- RUNNING:接收新任务,处理队列中的任务
- SHUTDOWN:不接收新任务,但能处理队列中的任务
- STOP:不接收新任务,不处理队列中的任务,并且会中断正在处理的任务
- TIDYING:所有的任务都已终止,正在执行的任务数量为0
- TERMINATED:线程池彻底终止
- 线程池原理(为什么能维持线程不释放,随时运行各种任务? )
// 执行过程
public void execute(Runnable command) {
// 如果任务为空,则抛出异常
if (command == null)
throw new NullPointerException();
// ctl:原子类变量,由线程池的运行状态和池内线程数量做运算得到
// c:原子类变量的值
int c = ctl.get();
// 获取池内线程数量,如果其小于池的核心容量
if (workerCountOf(c) < corePoolSize) {
// 添加一个核心线程来执行它的第一个任务,如果成功则返回
if (addWorker(command, true))
return;
// 没有成功,继续往下走
c = ctl.get();
}
// 如果线程池是运行状态,且成功把任务添加到队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 二次检查,如果线程池不是运行状态或者清除了新增的任务,抛出拒绝添加任务异常
if (! isRunning(recheck) && remove(command))
reject(command);
// 添加一个非核心线程,让它去队列中取任务执行
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 添加一个非核心线程来执行任务,如果失败,抛出拒绝添加任务异常
else if (!addWorker(command, false))
reject(command);
}
- 添加工作线程
- 启动工作线程
// https://blog.csdn.net/seasonLai/article/details/82624236
// addWorker()方法
// firstTask:第一个任务(根据新创建的线程而言)
// core:true表示创建核心线程,false表示创建非核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
// 循环标记
retry:
// 忙循环
for (;;) {
int c = ctl.get();
// 线程池运行状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 表示不允许创建新线程,返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 正在工作线程的数量
int wc = workerCountOf(c);
// 正在工作线程的数量达到限制值,返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 调用CAS将工作线程数量+1,跳出循环
if (compareAndIncrementWorkerCount(c))
break retry;
// 重新获取线程池运行状态,如果状态改变重新进入循环
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 工作线程
w = new Worker(firstTask);
//
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
// 线程池运行状态
int rs = runStateOf(ctl.get());
/ 线程池是运行状态,或者
if (rs < SHUTDOWN || SHUTDOWN 状态且任务为空
(rs == SHUTDOWN && firstTask == null)) {
// 如果执行线程处于就绪状态,抛出异常(因为这个时候我们还没有启动线程)
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 添加到工作线程集
workers.add(w);
// 工作线程数量
int s = workers.size();
// 记录线程池的线程峰值
if (s > largestPoolSize)
largestPoolSize = s;
// 表示成功添加工作线程
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 启动工作线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
- runWorker()方法:执行工作线程
- 获取任务、执行任务
- 如果有中断信号,则中断线程
// 启动工作线程后执行的方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 如果任务不为空,获取从队列中取到了任务
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
- getTask()方法:去队列拿任务
- 如果拿到任务则返回,如果拿不到任务则一直在这里循环,直到拿到任务为止
// getTask()方法
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 线程池优化
1、参考:https://www.cnblogs.com/dolphin0520/p/3932921.html
一般需要根据任务的类型来配置线程池大小:
如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1
如果是IO密集型任务,参考值可以设置为2*NCPU
当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。
2、参考:https://blog.csdn.net/wangdongli_1993/article/details/81268216
有经验公式:Nthread=Ncpu*Ucpu*(1+W/C)
W/C:等待时间与计算时间的比值
Ncpu:CPU数量
Ucpu:目标cpu的使用率
Java中下面方法获取CPU数目:
int Ncpus=Runtime.getRuntime().availableProcessors();
System.out.println(Ncpus);
- 线程池的优点
- 减少在创建和销毁线程上所花的时间以及系统资源的开销
- 如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存
- 线程组
- 作用:批量的管理线程或者线程组对象
- 用户创建的所有线程都属于指定线程组,如果没有显示指定属于哪个线程组,那么该线程就属于默认线程组(即main线程组)
- 虽然线程组看上去很有用处,实际上现在的程序开发中已经不推荐使用它了,主要有两个原因:
- 线程组ThreadGroup对象中比较有用的方法是stop、resume、suspend等方法,由于这几个方法会导致线程的安全问题(主要是死锁问题),已经被官方废弃掉了,所以线程组本身的应用价值就大打折扣了。
- 线程组ThreadGroup不是线程安全的,这在使用过程中获取的信息并不全是及时有效的,这就降低了它的统计使用价值。
- 虽然线程组现在已经不被推荐使用了,但是它在线程的异常处理方面还是做出了一定的贡献。当线程运行过程中出现异常情况时,在某些情况下JVM会把线程的控制权交到线程关联的线程组对象上来进行处理
//创建一个线程组对象
ThreadGroup threadGroup1 = new ThreadGroup("group1");
//获取当前线程所在线程组的名称
Thread.currentThread().getThreadGroup().getName()
//将一个线程放入线程组
Thread a = new Thread(group,new MyThread());
//复制线程组,将线程组group1中的线程复制到threads
Thread[] threads=new Thread[group1.activeCount()];
group1.enumerate(threads);
//线程组统一异常处理
//ThreadGroup内定义了一个方法:void uncaughtException(Thread t,Throwable e),该方法可以处理该线程组内的任意线程所抛出的未处理异常。
CountDownLatch seaphere=new CountDownLatch(1);
ThreadGroup group1=new ThreadGroup("group1"){
@Override
//重写自己的异常处理
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程名称:"+t.getName());
seaphere.countDown();
}
};
Thread t1=new Thread(group1,new MyThread());
t1.start();
seaphere.await();
//常用操作方法
int activeCount():获取线程组中活动线程的数量
interrupt():中断线程组中所有线程
isDaemon():是否为后台线程组
setDaemon(boolean daemon):设置为后台线程组
setMaxPriority(int pri):设置线程组的最高优先级
- 线程组和线程池的区别
- 线程组是为了方便线程的管理
- 线程池是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。
参考:
- Java并发编程:线程池的使用 https://www.cnblogs.com/dolphin0520/p/3932921.html
- 线程池为什么能维持线程不释放,随时运行各种任务 https://blog.csdn.net/cjh94520/article/details/70545202