学习笔记 2021.10.30续
2021.10.31
并发
线程池
线程池必考:三大方法、七大参数、四种拒绝策略
池化技术
程序的运行本质即是占用系统的资源,因此为了优化资源的使用,即引入了池化技术。
池化技术:事先准备好一些资源,需要用的时候就直接拿,用完之后归还即可。因为资源本身的创建、销毁等都十分复杂和占用时间。
线程池的好处
- 降低了资源的消耗
- 提高响应速度
- 方便管理
线程可复用、可以控制最大并发数、可以管理线程
线程池的三大方法
即创建线程池的三个可调用的方法,具体如下:
package JUC;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池的三大方法的测试
public class Demo11 {
public static void main(String[] args) {
ExecutorService ee = Executors.newSingleThreadExecutor();//单个线程的线程池
// ExecutorService ee = Executors.newFixedThreadPool(5);//创建一个固定大小的线程池
// ExecutorService ee = Executors.newCachedThreadPool();//可伸缩的线程池,可根据实际线程数量进行调整
try {
for (int i = 0; i < 10; i++) {
ee.execute(() -> {
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ee.shutdown();
}
}
}
分别的输出结果如下:



具体的理解就是创建好了线程池过后,通过execute方法就可以实现让池子里面的线程执行对应的run方法的这么一个过程,而具体用哪些线程则就是完全按照上面那三个方法来执行的了。
七大参数
通过看上面三个方法的源码可以发现:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
发现其实都是调用了ThreadPoolExecutor这个类来实现,而通过再去观察这个类的底层源码可以发现包含了以下七个关键参数

所以对于阿里开发手册:

这么要求的原因也就可以知道了,即是通过ThreadPoolExecutor能实现所有的方法,通过这样能够更好的去理解创建的细节。
最好创建线程池和线程就用这个方法!!
而对于七个参数的理解可以通过下图来认识:

具体的对应已经表在图中了,其中如果都满了还来人的话,就对应着拒绝策略这个参数。
如果max多出来的几个线程超过一定的时间还没有使用的话,则对其进行释放,这即对应着超时等待的参数。
手动创建线程池的示例
package JUC;
import java.util.concurrent.*;
//自定义线程池测试 及四大拒绝策略
public class Demo12 {
public static void main(String[] args) {
ExecutorService ee =new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 0; i < 2; i++) {
ee.execute(() -> {
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ee.shutdown();
}
}
}
其中执行后的结果也可以很明确的知道,
当池中容量小于核心加阻塞时,只会按照核心的数量去调用。
当池中容量大于核心加阻塞时并且小于最大加阻塞时,则按需打开池中的数量。
大于最大加阻塞时,则就进行拒绝策略了,这里的拒绝策略就是超过就报异常。
四种拒绝策略
AbortPolicy
DiscardPolicy
DiscardOldestPolicy
CallerRunsPolicy
其中第二个策略是队伍满了过后丢掉任务,但是这个就不会抛出异常了。
第三个是满了过后去尝试和最早的线程竞争,这也不会抛出异常。
第四个理解成从哪来到哪去,当线程池内的线程不够处理时,则交给上一级的线程去执行,这里应该一般都是主线程。
如下图所示
package JUC;
import java.util.concurrent.*;
//自定义线程池测试 及四大拒绝策略
public class Demo12 {
public static void main(String[] args) {
ExecutorService ee =new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
try {
for (int i = 0; i < 10; i++) {
ee.execute(() -> {
System.out.println(Thread.currentThread().getName()+"ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
ee.shutdown();
}
}
}

浙公网安备 33010602011771号