深入并发编程之线程池
前言
前两个章节的代码只是做个演示,其实是存在问题的,我们再来看一下:
public class TestThreadSafe {
int num = 0;
Lock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
TestThreadSafe t = new TestThreadSafe();
for (int i = 0; i < 100; i++){
new Thread(t::test).start();
}
TimeUnit.SECONDS.sleep(2);
System.out.println(t.num);
}
void test(){
lock.lock();
try {
num++;
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
1:我们是在循环中 new Thread,线程无法复用,这样会导致资源的浪费。
2:工作中多线程一般都是处理任务的,我们并不知道处理时间是多长,所以让主线程sleep是错误的。
线程池
1.1. 线程池的创建
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, // 核心线程数
2, // 最大线程数
60, // 线程存活时间
TimeUnit.SECONDS, // 存活时间单位
new LinkedBlockingQueue<>(),// 阻塞队列
Executors.defaultThreadFactory(),// 线程工厂
new ThreadPoolExecutor.AbortPolicy());// 拒绝策略
}
不推荐使用Executors创建,因为业务不同、部署在不同的服务器上,我们自定义可以更为的精细。
1.2. 线程池的使用
我们可以看到,线程池在执行任务的时候,execute 需要 Runnable ,submit 需要 Runnable 或 Callable 。
这里,我们需要回顾一下。
Runnable:无返回值,不能抛出异常。
Callable:有返回值,可以抛出异常。
1.3. execute底层实现
1:获取当前工作线程数。
2:如果当前工作线程数 < 核心线程数,则添加一个worker。
3:如果当前没有空闲线程,则判断以下当前线程池状态,如果时running,则进入队列,并且新增一个空任务的worker。
4:否则直接拒绝任务。
我们可以发现,所谓的addWorker就是创建了一个新的线程,然后调用start方法。那么run的是什么呢?我们继续往下看:
发现线程复用的原理时一个whille循环,它会不断的去取任务,如果没任务则会一直阻塞。