一,开篇:
java中提供了多线程设计的Api,为什么还要用线程池呢?
下来看两个例子:
1. 使用多线程跑十万次
2. 使用线程池跑十万次
使用多线程跑十万次
package com.laoyang.ThreadPool.公开课; import java.util.ArrayList; import java.util.Random; /** * @author:Kevin * @create: 2023-10-25 18:27 * @Description: 多线程跑十万次代码测试 */ public class ThreadDemo { public static void main(String[] args) throws InterruptedException { long currentTimeMillis = System.currentTimeMillis(); Random random = new Random(); ArrayList<Integer> list = new ArrayList<>(); for (int i = 0; i < 100000; i++) { Thread thread = new Thread() { @Override public void run() { list.add(random.nextInt(10)); } }; thread.start(); thread.join(); } System.out.println("时间:" + (System.currentTimeMillis() - currentTimeMillis)); System.out.println("大小:" + list.size()); } }
运行结果:
使用线程池跑十万次
package com.laoyang.ThreadPool.公开课; import java.util.ArrayList; import java.util.Random; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @author:Kevin * @create: 2023-10-25 18:28 * @Description: 多线程跑十万次测试 */ public class ThraedPollDemo { public static void main(String[] args) throws InterruptedException { long currentTimeMillis = System.currentTimeMillis(); Random random = new Random(); ArrayList<Integer> list = new ArrayList<>(); ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 100000; i++) { executor.execute(new Runnable() { @Override public void run() { list.add(random.nextInt(10)); } }); } executor.shutdown(); executor.awaitTermination(1, TimeUnit.DAYS); System.out.println("时间:" + (System.currentTimeMillis() - currentTimeMillis)); System.out.println("大小:" + list.size()); } }
运行结果:
可以看出两者简直天壤地别!!!
两者区别:
1. 第一种创建了100001个线程,但是第二种只创建了两个线程
为什么?
创建的线程越多,是对还是错? 肯定是错的
线程池的好处与不足?(OOM内存溢出,cpu-100%)
底层原理?
那为什么阿里巴巴又不推荐使用java自带的线程池呢?
二,线程池
1. 初次认识常见的线程池三种方式
package com.laoyang.ThreadPool.公开课; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author:Kevin * @create: 2023-10-25 18:50 * @Description: 初次认识线程池的几个构建方式 */ public class ThreadPoolMainTest { public static void main(String[] args) { ExecutorService executorService2 = Executors.newCachedThreadPool(); //快 ExecutorService executorService3 = Executors.newFixedThreadPool(10); //中 ExecutorService executorService1 = Executors.newSingleThreadExecutor(); //慢 for (int i = 0; i < 100; i++) { executorService2.execute(new Mytest(i)); } } } class Mytest implements Runnable{ private int i = 0; public Mytest(int i) { this.i = i; } @Override public void run() { System.out.println(String.format(Thread.currentThread().getName()+ "当前开始第 %s 个项目", i)); try { Thread.sleep(1000L); }catch (Exception e){} } }
可以发现执行速度从快到最慢
速度: newCachedThreadPool > newFixedThreadPool > newSingleThreadExecutor
假如将线程休眠代码注释,就会出现线程复用!
2. 剖析源码
newCachedThreadPool点进去一个就是ThreadPoolExecutor,那么现在来深度剖析下参数的每个意思。
SynchronousQueue:同步队列(同步机制)
可以发现只有非核心线程数,就是有一个任务,来一个非核心员工
newFixedThreadPool点进去也是ThreadPoolExecutor
可以发现核心线程数与最大线程数的值是一样的,说明只有核心线程数,没有额外的线程数
newSingleThreadExecutor点进去也是ThreadPoolExecutor
这个参数说明只创建了一个线程对象,个体户
3. 自定义线程池
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 20, 10L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
如果自定义的话会抛出异常,会在第31个抛出异常。原因:核心线程数10,最大线程数20,所以非核心线程数将是10个,同时阻塞队列大小为10,所以当阻塞队列慢的时候就会抛出异常。
那为什么执行的顺序为什么不一样,应该是1-10,11-20,21-30,但结果确相反?
原理:优先级 (核心线程>非核心线程>队列线程)