一、简易图帮助我们更好更快的理解线程池:
二、 理解线程池?
大家都遇到过一个问题,我们忙碌了一周之后,周末终于可以去享受一下。吃一顿自己最爱吃的火锅。结果女朋友化妆吗太磨叽,导致去了就1点多了,正是用餐的高峰期,火锅店一共有30个桌子,已经全部占满,而且还有10位在排队,你想放弃,但是拗不过你对象,只能等待了。
从上面的例子中可以看出,火锅店是一个线程池(准确来说房子是一个线程池)、顾客是将要执行的线程、当桌位满员,也就是核心线程达到最大数量的时候,我们只能在阻塞队列里面等待。当都满员的时候,老板想快速一点,会说,大家如果有不介意的,走廊也是可以坐的,这个走廊就好比我们除了核心线程池以外的区域也就是非核心线程。当非核心线程也满时,如果还有人继续的进入火锅店,老板会说,我们今儿人员已经爆满了,请您明日再来吧,这就是线程池抛出异常。
三、线程池的构造:
- 线程池的构造参数:corePoolSize:线程池中核心线程的数量。 maximumPoolSize:线程池中最大线程数量。 keepAliveTime:非核心线程的超时时长,当系统中非核心闲置时间超过该时间的时候被回收。 unit:该为keepAlivePoolSize的单位,有纳秒、微秒、毫秒、秒、分、时、天等 workQueue:线程池中的任务队列,用来存放已经被提交但尚未执行的任务。要用execute workQueue是一个BlockingQueue类型,当我们读取新的数据的时候,如果该队列是空的,则取数据的操作将会变成阻塞状态,当该队列有了新的数据的时候,再继续进行操作。当BloickingQueue , 队列中的队列满员的时候,也会变成阻塞状态, 有新的空间的时候就重新被唤醒。 1)、 ArrayBlockingQueue(数组队列):它的构造函数接收一个int类型的值,然后存在该数组队列。遵循先进先出的原则,规定了大小。 2)、LinkedBlockingQueue:(链队列):在其构造函数中接收一个int类型的值,这样可以来规定改队列的大小,如果不传的话,链表的长度不固定,最大值为max_value. 3)、PriorityBlockingQueue: 4)、SynchronousQueue:这个是同步线程的队列,属于线程安全。内部没有数据缓存空间。一对一的模式,互相牵制的对方 。 hreadFactory:为线程池创建新线程的功能。
四、 线程池的几种模式:
1、FixedThreadPool(我叫热水池):newFixedThreadPool来创建此线程,该线程中所有的线程都均为核心线程,没有超时机制,排队任务机制也无限制。相应比较快。
2、CachedThreadPool(我叫缓存池):通过newCacheThreadPool的方法来创建,为什么叫缓存池呢,不光是因为见名识意,因为它这个线程池中没有核心线程数,都是非核心线程,给我的感觉这些线程就是被存放在这个大池子中,有需要执行的线程的时候直接创建并分配线程进行执行。适合做一些无间断大量但是功耗比较小的任务。超时时间为60s,当线程闲置60s会自动被回收,所以也不会造成占用浪费资源的线程。
3、ScheduledThreadPool(超级池):为什么叫超级池呢,是因为它完美的融合了FixedThreadPool和CacheThreadPool这俩者。它不但有固定的核心线程数,而且还有无限量的非核心线程数。并且它的非核心线层的超时时间设置的为0s,一旦非核心线程空闲的时候就会回收,适合我们做一些定时的任务。比如前段时间项目中有querystatus就是可以使用。
4、SingleThreadExecutor(超累池):为什么叫超累池呢?因为它的池中只有一个核心的线程来执行任务,顺序执行,层层都需要自己把关,自己做,就想不会带团队的领导,累死也干不成大事。
五、线程池的实际应用:
//个人练习写的,望大神纠正
public class ExecuteThread {
//核心线程数
int corePoolSize;
//最大线程数=核心线程数+非核心线程数
int maximumPoolSize;
//非核心线程超时时间
long keepAliveTime=1L;
//非核心线程超时时间单位
TimeUnit unit=TimeUnit.MINUTES;
//线程池中的任务队列
BlockingQueue<Runnable> workQueue;
//线程工厂
ThreadFactory nThreadFactory;
private static ExecuteThread instance = null;
private ExecuteThread() {
}
public static ExecuteThread getInsatnce() {
if (instance == null) {
instance = new ExecuteThread();
}
return instance;
}
//创建线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
// AtomicInteger可以在并发情况下达到原子化更新,避免使用了synchronized,而且性能非常高
private final AtomicInteger mCount = new AtomicInteger(1);
@Override
public Thread newThread(@NonNull Runnable r) {
return new Thread(r, mCount.getAndIncrement() + "");
}
};
//核心线程池,创建的线程都是核心线程,没有超时机制,排队队列也无限制
public static ExecutorService newFixedThreadPool(int nThread) {
return new ThreadPoolExecutor(nThread, nThread,
keepAliveTime, unit, new LinkedBlockingQueue<Runnable>(), sThreadFactory);
}
}
//实际应用:
public void test(){
//直接调用该方法
ExecutorService executorService = ExecuteThread.newFixedThreadPool(5);
Runnable myRunnable = new Runnable() {
@Override
public void run() {
//执行我们一些耗时操作。
}
};
//提交到线程池
executorService.execute(myRunnable);
}
ps:细心的网友会发现会有这样的疑问,为什么你写了单例,为啥不用,而是直接类名.方法呢?哈哈,皮一下。是因为我在定义线程池的方法时把方法定义成了静态变量,所以直接类名.方法名。。。。。。去掉静态就可以使用单例来操作了。哈哈,不要打我。
延伸:private final AtomicInteger mCount = new AtomicInteger(1);
不知道大家有没有见过这个类,在上网浏览,查阅代码的时候看到的,然后简单的了解了一下。AtomicInteger是在非租塞算法实现并发控制。在并发的情况下达到原子化的更新,避免使用synchronized关键字,而且性能非常高。
六、后言:
之前也不是很了解线程池,停留在会用的层面,也一直么有总结,今儿就先总结一些简单层面的知识点。希望能帮助到大家,后续还会继续往深层次的研究有关于线程池的文章。