目录
1.线程池的作用
线程是一种有限的宝贵的系统资源,创建线程需要很多时间和系统资源,需要对线程进行回收利用,降低对系统资源的消耗,提升服务器的执行效率。
2.常用的线程池
顶层接口:
Executor
-
execute(Runnable) 将任务交给线程池执行
子接口:
ExecutorService
扩展:
-
shutdown() 停止线程池,会等待线程执行完
-
shutdownNow() 停止线程池,会终止线程执行
ExecutorService的子接口:ScheduledExecutorService
扩展:
-
在固定的延迟时间后执行
scheduleWithFixedDelay(执行的Runnable任务,初始的延迟,延迟,时间单位)
-
在固定的周期执行
scheduleAtFixedRate(执行的Runnable任务,初始的延迟,周期,时间单位)
工具类:对线程进行回收利用,降低对系统资源的消耗,提升服务器的执行效率。
Executors 提供了几个静态方法,帮助方便的创建线程池
线程池类型 | 说明 |
---|---|
ExecutorService newCachedThreadPool() | 创建线程个数不限的线程池 |
ExecutorService newFixedThreadPool(int) | 创建个数固定的线程池 |
ExecutorService newSingleThreadExecutor() | 创建单个线程的线程池 |
ScheduledExecutorService newScheduledThreadPool(int) | 创建可以调度的线程池 |
ExecutorService newWorkStealingPool() | 创建工作窃取机制的线程池 |
3.线程池的7个参数
线程池的实现类:
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 | 说明 |
---|---|
corePoolSize | 核心线程数(线程池初始的线程数,核心线程会一直保留) |
maximumPoolSize | 最大线程数(线程池最大线程数,非核心线程可能被干掉) |
keepAliveTime | 保持存活的时间(非核心线程如果在此时间内没有接收任务,就可能被干掉) |
unit | 时间单位 |
BlockingQueue<Runnable> | 用于保存任务的阻塞队列 |
ThreadFactory | 线程工厂,用于创新线程 |
RejectedExecutionHandler | 拒绝策略(任务数超过最大线程数,拒绝执行任务的方式) |
优化配置:
-
核心线程数 和cpu核心数以及任务数量和执行时间相关
核心线程数 = cpu核心数 * n
n >= 1 上限和任务数以及执行时间相关
本机的cpu核心数 Runtime.getRuntime().availableProcessors();
-
最大线程数
最大线程数和核心线程数相同,减少系统创建线程和销毁线程的时间和资源,提高性能
-
keepAliveTime
如果最大线程数和核心线程数相同,可以设置为0;
否则可以尽量大一点,减少系统创建线程和销毁线程的时间和资源
-
BlockingQueue
设置为LinkedBlockQueue,任务经常添加和删除时,性能更高
设置为SynchorousQueue,能处理的任务数更多,吞吐量更高(每秒处理请求数)
4.线程池的原理
4.1线程池总的执行过程
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
4.2线程池的线程是如何回收的
1) 通过线程池的execute执行任务
2) 通过调用线程池的addWorker方法添加工作线程
3) 同时把Runnable执行任务添加到workQueue中
4) 创建Worker添加到HashSet中,Worker中包含线程
5) 启动Worker中的线程
6) 线程调用runWorker方法
7) runWorker中判断任务不为空就执行任务
8) 任务如果为空,就调用getTask()方法取任务
9) 循环不断调用workerQueue阻塞队列的take()方法
10) 任务队列为空就自动阻塞当前线程,直到任务队列不为空唤醒线程去执行任务
5.线程相关的工具类
5.1CountDownLatch类
门栓,有时候一个线程的执行需要等待其它线程执行完后再执行
创建:
new CountDownLatch(倒数的数字)
主要方法:
void countDown() 倒数一次,数字减一
void await() 阻塞当前线程,当倒数为0就唤醒
public class CountDownLatchDemo {
public static void main(String[] args) {
//创建倒数对象
CountDownLatch latch = new CountDownLatch(3);
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"在等待其它线程!!");
try {
//等待其它线程结束
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" finished!!");
}).start();
for (int j = 0; j < 3; j++) {
new Thread(()->{
for(int i = 0;i < 5;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//倒数一次
latch.countDown();
System.out.println(Thread.currentThread().getName()+" finished,count=" + latch.getCount());
}).start();
}
}
}
5.2Semaphore 类
信号量,控制启动线程的数量
创建:
new Semaphore(最大数量)
常见使用方法:
aquired() 请求一个信号量
release() 释放一个信号量
public class SemaphoreDemo {
public static void main(String[] args) {
//创建信号量
Semaphore semaphore = new Semaphore(5);
for(int i = 0;i < 100;i++){
//启动线程
new Thread(() -> {
try {
//请求信号量
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"启动了");
Thread.sleep(1000);
//释放信号量
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}