线程成本
首先我们需要了解线程是不是越多越好?
1、线程在Java中是一个对象,每一个Java线程都需要一个操作系统线程支持。线程创建、销毁需要时间。如果创建时间+销毁时间>执行任务时间就很不合算。
2、Java对象占用堆内存,操作系统线程占用系统内存,根据JVM规范,一个线程默认最大栈大小1M,这个栈空间是需要从系统内存中分配的。线程过多,会消耗很多的内存。
从上面我们知道,线程创建,销毁需要时间,线程创建需要占用系统内存。线程的存在是有成本的,这就要看这个成本会不会影响系统性能了。
线程池构成
线程池中有任务,有线程。线程会把任务放入线程中执行。
如上图,线程池会接收任务,将任务放入仓库中;然后线程会从仓库中取任务,把那个将任务运送至工作内存中执行。当没有任务时,线程阻塞,有任务时线程被唤醒。
自定义线程池
我们需要一个集合用于存放线程。
1 2 | //1. 多个线程组成 private List<Thread> workers; |
然后再创建一个仓库,这个仓库是一个阻塞队列。
1 2 | //2.仓库 private BlockingQueue<Runnable> queue; |
需要一个开关来控制线程。
1 | private volatile boolean isWorking = true; |
编写线程类的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //poolSize: 工作线程数量 //taskSize: 任务数量 public FixedSizeThreadPool(int poolSize, int taskSize) { if (poolSize <= 0 || taskSize <= 0) { throw new IllegalArgumentException("非法参数"); } this.queue = new LinkedBlockingQueue<>(taskSize); //线程安全的list this.workers = Collections.synchronizedList(new ArrayList<>()); //创建线程,将线程放入集合中 for (int i = 0; i < poolSize; i++) { Worker worker = new Worker(this); worker.start(); workers.add(worker); } } |
创建一个线程类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //3.具体线程 public static class Worker extends Thread { private FixedSizeThreadPool pool; public Worker(FixedSizeThreadPool pool) { this.pool = pool; } @Override public void run() { while (pool.isWorking || pool.queue.size() > 0) { Runnable task = null; try { if (pool.isWorking) { task = this.pool.queue.take();//阻塞方式拿,拿不到会一直等待 } else { //非阻塞操作 task = this.pool.queue.poll(); } } catch (InterruptedException e) { e.printStackTrace(); } if (task != null) { task.run(); System.out.println("线程:" + Thread.currentThread().getName() + "执行完毕"); } } } } |
我们再编写一个执行任务的方法。
1 2 3 4 5 6 7 8 | public boolean execute(Runnable runnable) { if (isWorking) { //往阻塞队列中添加任务 return this.queue.offer(runnable); } return false; } |
关闭线程池。
1 2 3 4 5 6 7 8 9 | public void shutDown() { this.isWorking = false; for(Thread thread : workers){ if (Thread.State.BLOCKED.equals(thread.getState())) { thread.interrupt();//中断线程 } } } |
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Test public void testPool() { int taskSize = 6; FixedSizeThreadPool fixedSizeThreadPool = new FixedSizeThreadPool(3, taskSize); for (int i = 0; i < taskSize; i++) { fixedSizeThreadPool.execute(() -> { System.out.println("任务被放入了仓库"); try { Thread.sleep(2000L); } catch (InterruptedException e) { System.out.println("线程中断"); } }); } fixedSizeThreadPool.shutDown(); } |
上面的代码并不完善,有兴趣的可以自行完善。项目地址FixedSizeThreadPool.java
线程池数量多少合适
如果是计算型任务?
cpu数量的1-2倍
如果是IO型任务?
则需多一些线程,要根据具体的IO阻塞时长进行考量决定。如tomcat中默认的最大线程数为:200
也可考虑根据需要在一个最小数量和最大数量间自动增减线程数。
往期推荐
扫码二维码,获取更多精彩。或微信搜Lvshen_9,可后台回复获取资料
1.回复"java" 获取java电子书;
2.回复"python"获取python电子书;
3.回复"算法"获取算法电子书;
4.回复"大数据"获取大数据电子书;
5.回复"spring"获取SpringBoot的学习视频。
6.回复"面试"获取一线大厂面试资料
7.回复"进阶之路"获取Java进阶之路的思维导图
8.回复"手册"获取阿里巴巴Java开发手册(嵩山终极版)
9.回复"总结"获取Java后端面试经验总结PDF版
10.回复"Redis"获取Redis命令手册,和Redis专项面试习题(PDF)
11.回复"并发导图"获取Java并发编程思维导图(xmind终极版)
另:点击【我的福利】有更多惊喜哦。