目录
线程池是什么
线程池是一种基于池化思想管理线程的工具,设计它的核心目的是解决资源管理问题,可以降低资源消耗、提高响应速度以及提高线程的可管理性。
ThreadPoolExecutor
ThreadPoolExecutor是Executor最下层的实现类
- 参数1:corePoolSize:核心池大小
- 参数2:maximumPoolSize 线程池中允许的最大线程数
- 参数3:keepAliveTime 线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间,默认情况下,该参数只在线程数大于corePoolSize时才有用
- 最终任务执行完了,线程池中只会保留核心池大小数目的线程
- 参数4:unit 时间的单位 枚举类型
- 参数5:workQueue 工作的队列 存储任务的
- 参数6:threadFactory 线程工厂 用来创建线程的 因为线程池里可能会有多个线程,由线程工厂来创建
线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
- AbortPolicy:直接抛出异常,默认策略;
- CallerRunsPolicy:用调用者所在的线程来执行任务;
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
- DiscardPolicy:直接丢弃任务;
实现代码
public class ThreadTest {
public static void main(String[] args) {
// BlockingQueue<E> 是一个单端队列
// BlockingDeque<E> 是一个双端队列 可以从两端出队
LinkedBlockingQueue<Runnable> linkedBlockingQueue = new LinkedBlockingQueue<>(
100);// 100是任务队列的最大上限
/***
* 参数1:corePoolSize:核心池大小
*
* 参数2:maximumPoolSize最大池大小
*
* 参数3:keepAliveTime 保留存活的时间 如果过了这个存活时间,就会把除了位于核心池以外的线程集合中的其他线程给释放掉
* 最终任务执行完了,线程池中只会保留核心池大小数目的线程
*
* 参数4:unit 时间的单位 枚举类型
*
* workQueue 工作的队列 存储任务的
*
* threadFactory 线程工厂 用来创建线程的 因为线程池里可能会有多个线程,由线程工厂来创建
*/
ThreadFactory threadFactory = new ThreadFactory() {
// 线程安全的一个整数类 它是一个包装类 线程安全的一个整数包装类
// 参数 initialValue 是初始值
AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
// 创建一个线程 把r赋值给该线程
Thread thread = new Thread(r);
// 给线程起一个名称
// 线程池就是为了解决多线程的问题,
// 不能使用i++ 因为i++本身就是线程不安全的,因为i++底层的执行要分好几步
// newThread方法是并发的 ,同时可能有多个线程同时执行这个方法
// 也就有可能有多个线程同时执行i++,就有可能出现不同线程出现同样的名称
// 如果给本方法添加synchronized 可以,但是影响性能,而且synchronized粒度比较大,不太适合计数器这种情形
// atomicInteger.getAndIncrement() 先获取值 再自增
thread.setName("myThread:" + atomicInteger.getAndIncrement());
return thread;
}
};
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10,
1000, TimeUnit.MILLISECONDS, linkedBlockingQueue, threadFactory);
// maximumPoolSize+任务队列的大小为100 ,如果再多就会报拒绝异常
for (int i = 0; i < 110; i++) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
myMethod();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
// 加static 静态只能访问静态
public static void myMethod() throws InterruptedException {
System.out.println("ThreadName = " + Thread.currentThread().getName()
+ "进入方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadName = " + Thread.currentThread().getName()
+ "离开方法");
System.out.println("-----------------");
}
}
结果
需要注意的点
有2个需要注意的点
- 为什么我for循环了110次?
需要注意的是触发拒绝策略的条件是:线程数>线程池中允许的最大线程数+阻塞队列的大小
当前任务队列的大小为100,线程池中允许的最大线程数是10,所以是110
- 我们知道任务类型分为cpu密集型和io密集型,针对不同类型的任务我们要合理配置线程池
cpu密集型一般配置Ncpu+1个线程的线程池,io密集型一般配置2*Ncpu,这里的线程数量是指的是线程池中允许的最大线程数