一、环境
工欲善其事必先利其器,先创建环境,new Project --> 选好目录和Jdk版本号 --> 进去后新建module --> 也选好版本号。
二、背景介绍
如果不使用线程池,每个任务都会新开一个线程负责处理。比如现在这样:
public class EvertTaskOneThread {
public static void main(String[] args) {
Thread thread = new Thread(new Task());
thread.start();
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println("执行了该程序");
}
}
}
多个任务:
public class EveryTaskLoopThread {
public static void main(String[] args) {
for (int i = 0; i < 50; i++) {
Thread thread = new Thread(new Task());
thread.start();
}
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行了该程序");
}
}
}
但是使用线程池有以下好处:
- 避免反复创建线程,系统开销过大,创建、维护调用、关闭都会对系统有巨大影响
- 因为避免了创建和关闭的开销,响应速度变快了
三、创建和停止线程池
1. 线程池构造函数的参数
coolPoolSize和maxPoolSize
最好的例子就是银行排队:
一般银行不是所有窗口都开(maxPoolSize),而是只开启一部分,这就是corePoolSize,当超过当前corePoolSize,会把任务放到队列中去,放不下了才ru开启新线程去运行任务。如果队列满了,且达到了maxPoolSize就执行拒绝策略。
其他特点:
- 当coolPoolSize和maxPoolSize相同,就等于创建了固定大小的线程池(不会变)
- 线程池希望保持的数量少,只有负载很大的时候才增加它
- 当maxPoolSize设置为Integer.MAX_VALUE,可以容纳任意数量的并发任务
- 设置队列为无界队列(如LinkedBlockingQueue),线程就不会超过coolPoolSize
keepAliveTime
超过coolPoolSize达到一定程度会继续创建线程,但是只管创建不行,还得在不忙的时候回收掉,超过的时间就是keepAliveTime。
ThreadFactory
用来创建线程的,当要添加线程时就可以用这个。默认是使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组中,有相同的NORM_PRIORITY优先级,而且都不是守护线程。当然这个也可以自己设置。
查看源码:
Executors.defaultThreadFactory();
//最后是走这个方法
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
//创建现成的时候会调用newThread方法
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
workQueue
三种队列类型:
- 直接交接:SynchronousQueue
- 无界队列:LinkedBlockingQueue
- 有界队列:ArrayBlockingQueue
2. 手动创建还是自动创建线程池
手动创建可以字节设置规则,自动创建比较快捷。
JUC常见的线程池:
newFixThreadPool
public class FixedThreadPoolTest {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 1000; i++) {
service.execute(new Task());
}
}
static class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
}
不难发现,所有的程序都是在上面5个线程中执行的。
也可以看一下源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
这是其中的一些参数设置。
但是使用nexFixedThreadPool是有一些问题的,传进去的LinkedBlockingQueue容量没上限,当请求过多没法及时处理的时候,内存占用过多就会报OOM。
public class FixedThreadPoolOOM {
private static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
executorService.execute(new SubThread());
}
}
}
class SubThread implements Runnable{
@Override
public void run() {
try {
Thread.sleep(1000000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
最后会报错:
说明这种方式是有瑕疵的。
newSingleThreadPool
示例:
public class SingleThreadExecutor {
public static void main(String[] args) {
ExecutorService service = Executors.newSingleThreadExecutor();
for (int i = 0; i < 1000; i++) {
service.execute(new FixedThreadPoolTest.Task());
}
}
}
源码如下:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
这个线程池和上面newFixThreadPool也很类似,只是线程数是1。
newCachedThreadPool
特点是可以缓存线程池,具有自动回收多余线程的功能。
先看源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
前面介绍了SynchronousQueue是一个直接交换的队列,也就是队列容量是0,也就是来了任务会直接交给线程执行,没用到的线程会被回收掉。
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
service.execute(new FixedThreadPoolTest.Task());
}
}
}
结果如下:
这里的问题是,可能会创建非常多的线程,造成OOM。
newScheduledThreadPool
先看源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
支持定时及周期性任务执行的线程池。
public class ScheduledTreadPool {
public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
service.schedule(new FixedThreadPoolTest.Task(),5, TimeUnit.SECONDS);
}
}
这里是可以设置隔一段时间执行的,执行上面的指令可以看到。
语法:service.schedule(task,time,timeunit);
线程池的线程数量设置为多少合适?
主要还是corePoolSize和maxPoolSize:
- CPU密集型:最佳线程数为CPU核心数的1-2倍左右
- 耗时IO型:最佳线程数一般大于CPU核心数很多倍
线程数=CPU核心数×(1+平均等待时间/平均工作时间)。
线程池的对比
上面的意思是60sec后回收。ScheduledThreadPool的keepAliveTime是0sec,上面写错了。
workStealingPool
Java1.8的时候加入的。用的比较少,任务可以产生子任务可以使用这个,比如树的查询,矩阵的划分等,这种线程池有窃取的能力,比如A线程产生多个子任务,子任务是分配到线程自己的队列中去执行的,如果A线程中任务有点多,别的线程空闲时可以帮忙处理。
3、停止线程
几个方法:
shutdown
停止线程,但并不一定真的停止,只是初始化停止线程的步骤,接下来再有任务过来直接拒绝
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author :16140
* @description :
* @create :2022-08-02 22:24:00
*/
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
service.execute(new ShutDownTask());
}
//让它运行1.5s
Thread.sleep(1500);
//shutdown该线程看看是否真的关闭了
service.shutdown();
//测试shutdown了以后,再交线程会不会报错
service.execute(new ShutDownTask());
}
}
class ShutDownTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果确实,shutdown以后再执行线程报错:
isShutDown
判断线程是否shutdown了
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author :16140
* @description :
* @create :2022-08-02 22:24:00
*/
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
service.execute(new ShutDownTask());
}
//让它运行1.5s
Thread.sleep(1500);
System.out.println(service.isShutdown());
//shutdown该线程看看是否真的关闭了
service.shutdown();
System.out.println(service.isShutdown());
//测试shutdown了以后,再交线程会不会报错
service.execute(new ShutDownTask());
}
}
class ShutDownTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
isTerminated
查看线程是否完全终止了
service.isTerminated();
再试一下,当所有任务执行完毕是否为true:
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author :16140
* @description :
* @create :2022-08-02 22:24:00
*/
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
service.execute(new ShutDownTask());
}
//让它运行1.5s
Thread.sleep(1500);
System.out.println(service.isShutdown());
//shutdown该线程看看是否真的关闭了
service.shutdown();
System.out.println(service.isShutdown());
System.out.println(service.isTerminated());
Thread.sleep(10000);
System.out.println(service.isTerminated());
//测试shutdown了以后,再交线程会不会报错
// service.execute(new ShutDownTask());
}
}
class ShutDownTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
isShutdown只是判断是否开始停止了,isTerminated是判断是否完全终止了。
awaitTermination
等待一段时间,看线程是否彻底终止了,这个方法会把这个线程锁住,(不是执行的线程)
System.out.println(service.awaitTermination(10L, TimeUnit.SECONDS));
shutdownNow
把线程立刻关闭
package threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author :16140
* @description :
* @create :2022-08-02 22:24:00
*/
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
service.execute(new ShutDownTask());
}
//让它运行1.5s
Thread.sleep(700);
service.shutdownNow();
class ShutDownTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
}
结果如图:
返回runnableList: