多线程2
文章最后更新时间:2025年05月16日
猜测一下,打印的“线程测试”的两个方法 那个是多线程执行的?
/** * @author : zanglikun * @date : 2021/12/31 15:25 * @Version: 1.0 * @Desc : 继承Thread 重写run方法 */public class ThreadDemo extends Thread{@Overridepublic void run() { System.out.println("线程测试"); }public static void main(String[] args) {new ThreadDemo().run();new ThreadDemo().start(); } }
答案是 start()执行的是多线程的。
我们Debug看一下:
run()方法

进入断点之后点击 照相机(获取线程转储) 就可以看到当前项目的线程信息了!

Start()方法

对比一下,run方法的线程名叫main 而 start方法执行的线程名是 Thread-0@509
在对比一下 单线程与多线程的执行效率
单线程代码
@SneakyThrows@Testpublic void testSingleThread() {long StartTime = System.currentTimeMillis();final ArrayList<String> arrayList = new ArrayList();for (int i = 0; i < 100000; i++) {Thread thread = new Thread() {@Overridepublic void run() { arrayList.add("abcdfg"); } }; thread.start();// join()让Thread这个线程 在main线程结束时等待,让其在main线程结束后在结束!thread.join(); }long EndTime = System.currentTimeMillis(); System.out.println("消耗了:" + (EndTime - StartTime) / 1000.00 + "秒"); System.out.println(arrayList.size()); }
多线程代码
@SneakyThrows@Testpublic void testMuiltThread() {long StartTime = System.currentTimeMillis();final ArrayList<String> arrayList = new ArrayList();ExecutorService executorService = Executors.newSingleThreadExecutor();for (int i = 0; i < 100000; i++) { executorService.execute(() -> { arrayList.add("abcdfg"); }); }// 如果不shutdown,那么main方法走完了,直接打印消耗时间、打印集合长度时不准确的。因为main线程结束,线程池的线程还在执行,就打印结果肯定是不准确的executorService.shutdown(); executorService.awaitTermination(1, TimeUnit.DAYS);long EndTime = System.currentTimeMillis(); System.out.println("消耗了:" + (EndTime - StartTime) / 1000.00 + "秒"); System.out.println(arrayList.size()); }
测试不同线程池的效率
@SneakyThrows@Testpublic void testDifferentThreadPool() {long StartTime = System.currentTimeMillis();final ArrayList<String> arrayList = new ArrayList();ExecutorService executorService1 = Executors.newCachedThreadPool(); //最快ExecutorService executorService2 = Executors.newFixedThreadPool(10); //慢大约49倍的执行时间(只在本场景(循环1000次)适用,不是两个线程池的的规范的效率差距)ExecutorService executorService3 = Executors.newSingleThreadExecutor(); //对比上一个慢了10倍(只在本场景(循环1000次)适用,不是两个线程池的的规范的效率差距)for (int i = 0; i < 1000; i++) { executorService1.execute(() -> {try { System.out.println("当前线程名称"+Thread.currentThread().getName()); arrayList.add("abcdef"); Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } }); }// 如果不shutdown,那么main方法走完了,直接打印消耗时间、打印集合长度时不准确的。因为main线程结束,线程池的线程还在执行,就打印结果肯定是不准确的executorService1.shutdown(); executorService1.awaitTermination(1, TimeUnit.DAYS);long EndTime = System.currentTimeMillis(); System.out.println("消耗了:" + (EndTime - StartTime) / 1000.00 + "秒"); System.out.println(arrayList.size()); }
使用不同线程池分别执行一下,结果:
newCachedThreadPool() 0.616秒 打印日志如下: 当前线程名称pool-1-thread-1 当前线程名称pool-1-thread-3 当前线程名称pool-1-thread-2 ...... 当前线程名称pool-1-thread-997 当前线程名称pool-1-thread-998 当前线程名称pool-1-thread-999 当前线程名称pool-1-thread-1000 ... 线程ID 编号取值1-1000 newFixedThreadPool() 50.037秒 打印日志如下: 当前线程名称pool-2-thread-1 当前线程名称pool-2-thread-2 当前线程名称pool-2-thread-3 当前线程名称pool-2-thread-4 当前线程名称pool-2-thread-6 当前线程名称pool-2-thread-5 当前线程名称pool-2-thread-7 当前线程名称pool-2-thread-8 当前线程名称pool-2-thread-10 当前线程名称pool-2-thread-9 ... 以此循环线程ID 编号取值1-10 这里就能看到线程复用的提现! newSingleThreadExecutor() 我预测是500秒,没想到还真是500.306秒 打印日志如下: 当前线程名称pool-3-thread-1 当前线程名称pool-3-thread-1 当前线程名称pool-3-thread-1 ... 线程ID 编号取值都是1
三种线程池为什么有这么大的差距?
分别看一下这三种线程池源码的构造方法
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE, #MAX_VALUE是 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())); }
构造方法里核心线程数、最大线程数,也就是前2个参数区别(阻塞队列这里我们忽略不考虑)!造成了这个场景的效率差距!
newCachedThreadPool 最大允许开启21亿个线程处理任务 newFixedThreadPool 最大允许开启10个线程处理(我们上方创建对象传入的10) newSingleThreadExecutor 最大允许开启1个线程处理任务 所以 对应的结果日志时间上的比例也是如此了 第一次 线程直接处理完毕了,而第二次同期时间才处理了10个 剩余990任务都是阻塞的,所以第一次比第二次早了 990 *0.5睡眠的时间 也就是大约48.5秒 第二次 比第三次线程也多了10倍 所以第二次比第三次消耗的时间的10倍。<div style="height:100px" ari
文章版权声明:除非注明,否则均为八一构原创文章,转载或复制请以超链接形式并注明出处。
还没有评论,来说两句吧...