Java并发线程池原理源码深入分析与调优实战

一,开篇:

java中提供了多线程设计的Api,为什么还要用线程池呢?

下来看两个例子:

  1. 使用多线程跑十万次

  2. 使用线程池跑十万次

使用多线程跑十万次

java 复制代码
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());

    }
}

运行结果:

使用线程池跑十万次

java 复制代码
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. 初次认识常见的线程池三种方式

java 复制代码
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. 自定义线程池

java 复制代码
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 20,
                10L, TimeUnit.SECONDS, new ArrayBlockingQueue(10));

如果自定义的话会抛出异常,会在第31个抛出异常。原因:核心线程数10,最大线程数20,所以非核心线程数将是10个,同时阻塞队列大小为10,所以当阻塞队列慢的时候就会抛出异常。

那为什么执行的顺序为什么不一样,应该是1-10,11-20,21-30,但结果确相反?

原理:优先级**(核心线程>非核心线程>队列线程)**

相关推荐
云烟成雨TD3 小时前
Spring AI Alibaba 1.x 系列【69】Token 用量统计
java·人工智能·spring
JAVA9653 小时前
JAVA面试-并发篇 03-使用synchronized doublecheck实现单例有什么坑
java·单例模式·面试
在繁华处3 小时前
Java从零到熟练(四):面向对象基础
java·开发语言
Unbelievabletobe3 小时前
解决了股票api接口盘后数据更新慢的问题
大数据·开发语言·python
不会C语言的男孩4 小时前
C++ Primer 第2章:变量和基本类型
开发语言·c++
小江的记录本4 小时前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
在繁华处4 小时前
Java从零到熟练(三):流程控制
java·开发语言·python
唐青枫5 小时前
Java Optional 实战指南:优雅处理空值与链式转换
java
一起学开源5 小时前
一文读懂 ReAct 范式:让 AI Agent 真正学会“思考+行动“
java·javascript·react.js·ecmascript·react·alibaba·智能体开发
云泽8085 小时前
C++ 可调用对象通关指南:深度解析 Lambda 表达式、function 包装器与 bind 绑定器
开发语言·c++·算法