1、总共有5种线程池:
1、单个线程
ExecutorService pool1 = Executors.newSingleThreadExecutor();
2、固定数量线程
ExecutorService pool2 = Executors.newFixedThreadPool(2);
3、动态线程数量
ExecutorService pool3 = Executors.newCachedThreadPool();
4、定时调度的线程池,功能和timer一样
ExecutorService pool4 = Executors.newScheduledThreadPool(2);
ScheduledThreadPoolExecutor还是继承了ThreadPoolExecutor
5、这个不知道是什么线程池
ExecutorService pool5 = Executors.newWorkStealingPool();
它们都有execute()、submit()等方法。
在getTask中有这么一段代码
我们执行它的execute()方法其实只是把任务放在它里面的blockingqueue中,这样就会唤醒阻塞的线程拿到task来执行,所以这就需要blockingqueue。这是一个生产者和消费者模型
2、问题:怎么知道queue中的任务怎么运行?
这就需要统一的接口标准,往里面扔的任务必须implements Runable接口才行(为什么?)
因为:只要实现了Runable/callable接口,就必须实现一个run/call方法,这样线程池中的线程就可以直接调用run或者call方法,执行业务。
注意:传入进去的Runnable任务,不会以线程的形式运行。
3、代码实例:
import java.util.ArrayList;
import java.util.concurrent.*;
/**
* Executors创建线程池
* ThreadPoolExecutor创建线程池
*
* 类的关系
* class ThreadPoolExecutor extends AbstractExecutorService
*
* class AbstractExecutorService implements ExecutorService
*
* interface ExecutorService extends Executor
*
* [@Author](https://my.oschina.net/arthor) liufu
* @CreateTime 2018/5/10 15:24
*/
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ExecutorService pool1 = Executors.newFixedThreadPool(2);
ExecutorService pool2 = Executors.newCachedThreadPool();
ExecutorService pool3 = Executors.newScheduledThreadPool(2);
ExecutorService pool4 = Executors.newSingleThreadExecutor();
ExecutorService pool5 = Executors.newWorkStealingPool();
//Executors.newXX其实就是创建了new ThreadPoolExecutor()对象,里面传入对应的值而已
//推荐使用这种方式,对资源好控制
ExecutorService executor = new ThreadPoolExecutor(2, //最小线程数
2, //最大线程数
0L, //执行超时,0L,表示不超时
TimeUnit.MILLISECONDS, //时间为分钟
new ArrayBlockingQueue<Runnable>(10)); //任务队列的大小
ArrayList<Runnable> runnables = new ArrayList<>();
pool1.shutdown();
//这个就是生产者!!
for (int i = 0; i < 100; i++) {
runnables.add(new Runnable() {
[@Override](https://my.oschina.net/u/1162528)
public void run() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("工作完成!!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
//消费者!!!
for (Runnable runnable : runnables) {
pool1.execute(runnable);
}
//主线程是不等待线程池的。
System.out.println("早就执行完了,不等待线程池的处理了");
}
}
4、线程池示意图
5、问题
1、为什么需要线程池?
这个和连接池一样,为了避免频繁的创建和销毁线程池带来的性能损耗。
2、它的execute()方法其实只是把任务放在它里面的BlookingQueue中,然后线程池里面的线程来获取任务执行,如果queue中没有任务就阻塞。
3、线程池创建的方式
3.1、Excutors
内部new ThreadPoolExcutor(),而且那个任务队列queue是没有指定大小的,默认是Integer.MAX_VALUE = 42亿,如果线程处理的慢,就会导致queue堆满42亿任务对象,会出现内存溢出问题,GC没法扫。
3.2、new ThreadPoolExcutor()这种比较自主可控,里面有7个参数
前面5个比较常用分别是:
最小线程 最大线程 过期时间 时间单位 queue(要指定大小)
后面是:
ThreadFactory(可以给线程加名字) RejectedExecutionHandler(拒绝执行时的回调)。
RejectedExecutionHandler已经有好几种实现
● AbortPolicy ● DiscardPolicy ● DiscardOldestPolicy ● CallerRunsPolicy ● 自定义