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大参数意义
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 | 说明 |
---|---|
corePoolSize | 核心线程数(线程池初始的线程数,核心线程会一直保留) |
maximumPoolSize | 最大线程数(线程池最大线程数,非核心线程可能被干掉) |
keepAliveTime | 保持存活的时间(非核心线程如果在此时间内没有接收任务,就可能被干掉) |
unit | 存活时间的单位 |
BlockingQueue | 用于保存任务的阻塞队列 |
ThreadFactory | 线程工厂,用于创建线程 |
RejectedExecutionHandler | 拒绝策略(任务数超过最大线程数,拒绝执行任务的方式) |
参数的配置
-
核心线程数
和cpu核心数以及任务数量和执行时间相关
核心线程数 = cpu核心数 * n (n >= 1) 上限和任务数以及执行时间相关
可以使用Runtime.getRuntime().availableProcessors()获得本机的CPU核心数
-
最大线程数
最大线程数和核心线程数相同,减少系统创建线程和销毁线程的时间和资源,提高性能
-
keepAliveTime
如果最大线程数和核心线程数相同,可以设置为0;
否则可以设置大一点,减少系统创建线程和销毁线程的时间和资源
-
BlockingQueue
设置为LinkedBlockQueue,任务经常添加和删除时,性能更高
设置为SynchronousQueue,能处理的任务数更多,吞吐量(每秒处理请求数)更高
-
ThreadFactory、RejectedExecutionHandler
使用默认配置即可
4. 线程池的原理
线程池的执行过程
(1)执行任务时判断线程数是否小于核心线程数
(2)如果小于核心线程数就添加核心线程
(3)如果超过核心线程数,就判断是否小于最大线程数
(4)如果小于最大线程数就添加非核心线程
(5)如果超过最大线程数,就执行拒绝策略
线程池的线程如何回收
(1)通过线程池的execute方法执行任务
(2)通过调用线程池的addWorker方法添加工作线程,同时把Runnable执行任务添加到workQueue中
(3)创建Worker添加到HashSet中,Worker中包含线程
(4)启动Worker中的线程,线程调用runWorker方法
(5)runWorker中判断当前任务是否为空,不为空就执行任务;如果为空,就调用getTask方法取出任务
(6)循环不断调用workerQueue阻塞队列的take方法
(7)任务队列为空就自动阻塞当前线程,直到任务队列不为空唤醒线程去执行任务
5. 线程的工具类
CountDownLatch类
门栓,有时候一个线程的执行需要等待其它线程执行完后再执行
创建:
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();
}
}
}
Semaphore 类
信号量,控制启动线程的数量
创建:
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();
}
}
}