线程池
java中线程的机制:
1、java中线程的执行是抢占式执行,哪个线程抢到,在规定的时间(java有规定的时间片),就是这个线程执行,执行结束(也就是时间片),就进入就绪状态(java中其实没有就绪状态和运行时状态的说法,规范统一都是runnable状态,为了让大家好理解才说就绪和运行时状态)。
2、在多线程多任务的情况下,需要创建多个线程,同时最后也会有多个线程的“死亡”(线程的创建和死亡也是需要时间的:)
暴露出来的问题:
1,多任务执行的时候,线程的不断创建,不断关闭,就会过度的消耗cpu资源
2、线程一多,频繁的切换线程,可能会导致系统崩溃
3、总通俗的话来解释上面:就是以传统的多线程执行任务,多少个任务就会创建多少个线程,同时到最后结束的时候,就会有多少个线程死亡,(假如:某个任务执行的时间为很短,短如10ms,那么便会为了10ms创建专门一个线程,这显然是不合理的,又占用cpu资源)
4、因此线程池就是为了解决以上暴露的问题而出来的。
什么是线程池
1、线程池:顾名思义:存放线程的池子,就尼玛的这么通俗易懂。
线程池的使用:将作业任务放到队列里面,线程池里面的空闲资源会去执行队列里面的任务。执行完毕之后线程再次空闲(“非核心线程存活是有时间的哦”);
线程池里面的线程:两类:
核心线程:
- 1、在线程池创建的时候就已经创建了
- 2、即使所有任务执行完毕,也不会销毁(直到虚拟机关闭或者手动关掉线程池,线程才会销毁)
- 3、其数量由线程池的实现类确定,也可以自己写一个线程池(也称之为自定义线程池)
非核心线程:
- 1、在线程创建的时候不会创建,而是当核心线程都不空闲,而任务需要执行,才会创建
- 2、其数量由线程池的实现类确定
- 3、其存活时间由线程池的实现类确定。
一个简单的自定义线程池:
先上代码,再解释:
package 博客.线程池;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Application {
public static void main(String[] args) {
// 自定义线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(8,12,1,TimeUnit.SECONDS,new ArrayBlockingQueue(10));
// pool.execute(() -> {
// System.out.println("自定义线程池");
// });
pool.execute(new Runnable() {
@Override
public void run() {
System.out.println("自定义线程池");
}
});
}
}
运行结果:
分析:
运行结果来看:虚拟机并没有被关闭,也就意味着,还有线程存活着
第一步分析:
ThreadPoolExecutor pool = new ThreadPoolExecutor(8,12,1,TimeUnit.SECONDS,new ArrayBlockingQueue(10));
表示的是自定义线程池:核心线程:8,非核心线程::最多可以创建:12-8=4个,非核心线程最多存活的时间为:1分钟,所用的数组是ArrayBlockingQueue(10)):
因为核心线程不死原则(pool关闭之前)以及非核心线程没有任务之后一分钟才死亡原则,所以虚拟机没有关闭
第二步分析:
为什么要使用线程池呢?
1、以上一个例子线程池为例:当任务进入线程池,
|—任务先交给空闲的核心线程
|—核心线程执行完毕,又进入空闲状态,等待下一个任务的到来
|—如果多任务进来,核心线程不够用,便会创建非核心线程,分担核心线程的压力,但是非核心线程数和空闲最长时间由我们掌控
这样做的好处1、:由于线程数由我们掌控,避免了过多线程的创建
2:由于线程执行任务完毕之后,不会死亡,而是等待下一个任务的到来,避免了过多线程的创建,也避免了线程的过快创建和过快死亡(降低了线程的死亡和创建给cpu带来的压力)
在我们的使用中,我们不需要一般不需要自定义线程池
1根据实际需求选择合适的线程池就ok了
创建线程池:需要用到Executes这个类
-
Executes.newCachedThreadPool ():
没有核心线程,只有非核心先吃,线程空闲存活时间为一分钟
应用场景:任务过多且需要不断创建线程的时候 -
Executes.newFixedThreadPool(int a)
只有核心线程:必须传入一个数值
可以控制最大并发数,防止创建过多的线程 -
Executes.newSingleThreadExecutor()
只有一个线程,且是核心线程
这样确定了任务的执行顺序。 -
Executes. newScheduledThreadPool ()(挺重要的,在项目开发的时候常用)
指定核心线程数,也只有核心线程
这个线程池和上面有点不同,父类引用是ScheduledExecutorService
由于父类引用的差异性:多出了以下两种功能
1、延迟执行
2、指定周期执行
延迟执行
调用的执行方法不是 execute()了,而是scheduleAtFixedRate(Runnable , int ,TimeUtile)
所以可以延迟时间执行
指定周期执行:
scheduleAtFixedRate(Runnable对象,2,1,TimeUtile.SECONDS)
表示的是:延迟两秒执行,
没隔一秒执行一次
最后:
希望这篇博客能对各位朋友有用,有什么问题请即使提出,谢谢