⭐ 作者:小胡_不糊涂
作者主页:小胡_不糊涂的个人主页
收录专栏:JavaEE
持续更文,关注博主少走弯路,谢谢大家支持
想象这么⼀个场景:
在学校附近新开了⼀家快递店,⽼板很精明,想到⼀个与众不同的办法来经营。店⾥没有雇⼈,⽽是每次有业务来了,就现场找⼀名同学过来把快递送了,然后解雇同学。这个类⽐我们平时来⼀个任务,启动⼀个线程进⾏处理的模式。
很快⽼板发现问题来了,每次招聘 + 解雇同学的成本还是⾮常⾼的。⽼板还是很善于变通的,知道了为什么⼤家都要雇⼈了,所以指定了⼀个指标,公司业务⼈员会扩张到3个⼈,但还是随着业务逐步雇⼈。于是再有业务来了,⽼板就看,如果现在公司还没3个⼈,就雇⼀个⼈去送快递,否则只是把业务记下来,等着3个快递⼈员空闲的时候去处理。这个就是我们要带出的线程池的模式。
线程池最⼤的好处就是减少每次启动、销毁线程的损耗。
在Java中可以使⽤Executors.newFixedThreadPool(10) 能创建出固定包含10个线程的线程池。返回值类型为 ExecutorService,然后通过 ExecutorService.submit 可以注册⼀个任务到线程池中。
public static void main(String[] args) {
ExecutorService service=Executors.newFixedThreadPool(10);
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello");
}
});
}
Executors 创建线程池的几种⽅式:
ThreadPoolExecutor 提供了更多的可选参数,可以进⼀步细化线程池⾏为的设定:
主要思路:
1.提供构造方法,指定创建多少个线程
2.在构造方法中,把这些线程都创建好
3.有一个阻塞队列,能够持有要执行的任务
4.提供submit方法,可以添加新的任务
class MyThreadPoolExecutor{
private List<Thread> threadList=new ArrayList<>();//保存产生的线程
private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>(1000);//保存任务的队列
//通过n指定创建多少个线程
public MyThreadPoolExecutor(int n) {
for (int i = 0; i < n; i++) {
//取出任务并执行
Thread t = new Thread(() -> {
while(true){
try {
Runnable runnable=queue.take();///如果队列为空,take就会阻塞
runnable.run();//取出一个执行一个
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
threadList.add(t);
}
}
//把任务传到队列中
public void submit(Runnable runnable) throws InterruptedException {
queue.put(runnable);
}
}
测试类:
public class TestDemo9 {
//public static volatile int i=0;
public static void main(String[] args) throws InterruptedException {
MyThreadPoolExecutor executor = new MyThreadPoolExecutor(4);
for (int i = 0; i < 10; i++) {
int n = i;//防止变量捕获(不可以是一直变化的值)-->volatile
executor.submit(new Runnable() {
@Override
public void run() {
System.out.println("执行任务" + n + "当前任务的线程为:" + Thread.currentThread().getName());
}
});
}
}
}
多个线程之间的执行顺序是不确定的,某个线程取到任务也并非是立即就执行,就会产生无序的结果。但此处这些线程都是等价的。