11 Java线程池详解

线程池使用及优势

线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在创建后启动这些任务,如果线程数据超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行

他的主要特点为:线程复用 控制最大并发数 管理线程

第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁的消耗

第二:提高响应速度。当任务到达时任务可以不需要的等到线程创建就能立即执行。

第三:提高线程的可管理性。线程时稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

image-20220209115107928
1.1 常用的线程池
  1. newFixedThreadPool 执行长期的任务,性能好很多

  2. newSingleThreadExecutor 一个任务一个任务执行的场景

  3. newFixedThreadPool 执行很多短期异步的小程序或者负载较轻的服务器

1.2 线程池七大参数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ydbpp8vU-1644397721463)(/Users/chensihao/Library/Application Support/typora-user-images/image-20220209133954431.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tswLn9BF-1644397721464)(/Users/chensihao/Library/Application Support/typora-user-images/image-20220209140847725.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RrN3ozog-1644397721464)(/Users/chensihao/Library/Application Support/typora-user-images/image-20220209141705719.png)]

1.3 JVM拒绝策略

当最大线程池已满,阻塞队列已满,线程池会使用拒绝策略

  1. Abort(默认):直接抛出RejectExecutorException异常阻止系统正常运行

  2. CallerRunsPolicy: 调用者运行一种机制,该策略及不回抛弃任务,也不会抛出异常,而是将某些任务回退到调用者(由调用者来执行),从而降低新任务的流量。

  3. DiscardOldestPolicy: 抛出队列中等待最久的任务,然后把当前任务加入队列中尝试再次提示当前任务

  4. DiscardPolicy: 直接丢弃任务,不予任务处理也不抛出异常。如果允许任务丢失,这是最好的一种方案

线程池在实际中使用哪一个

线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,避免资源耗尽的风险。

说明:Executor放回的线程池对象的弊端如下:

1)Fixed ThreadPool和SingleThreadPool:

​ 允许的请求队列的长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

2)CachedThreadPool和ScheduleThreadPool

允许的创建线程数量为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。

1.4 手写线程池改造和拒绝策略
public class ExecutorPoolThreadDemo {

    public static void main(String[] args) {
        ExecutorService executor = new ThreadPoolExecutor(
                2,//核心处理数
                5, // 最大线程数
                1,// 空闲时间
                TimeUnit.SECONDS, // 时间单位
                new LinkedBlockingQueue<>(3),// 创建阻塞队列
                Executors.defaultThreadFactory(), // 默认线程工程
                new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略

        try{
            for (int i = 0; i < 20; i++) {
                executor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" 办理业务");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executor.shutdown();
        }
    }
}
1.5 合理配置线程池线程数

CPU密集型

​ CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一致全速运行

​ CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程)

​ 而在单核CPU上无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。

​ CPU密集型任务配置尽可能少的线程数量:一般公式:CPU核数 + 1个线程的线程池

IO密集型

  1. 由于IO密集型任务线程并不是一直执行任务,则应配置尽可能多的线程,如CPU核数 * 2

  2. IO密集型,即该任务需要大量的IO,即大量的阻塞,在单线程上运行IO密集型的任务会导致大量的CPU浪费在等待。所以在IO密集型任务中使用多线程可能大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间

    IO密集型时,大部分线程都阻塞,故需多配置线程数

    参考公式:CPU核数 /(1 - 阻塞系数) (阻塞系统在0.8~0.9)

    比如8核CPU:8 /(1 -0.9 )= 80个线程数

1.6 死锁编码及定位分析

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们将无法推进下去,如果系统资源充足,进程的资源请求都能狗得到满足,死锁的出现可能性就很低,否则就会因争夺有限的资源而陷入死锁。

image-20220209161433999

1. 线程死锁案例

class HoldLockThread implements Runnable{

    private String lockA;
    private String lockB;

    public HoldLockThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }


    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+" 自己持有"+lockA+" 尝试获得"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+" 自己持有"+lockB+" 尝试获得"+lockA);
            }
        }
    }
}

public class DeadLockDemo {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new HoldLockThread(lockA,lockB)).start();
        new Thread(new HoldLockThread(lockB,lockA)).start();
    }
}
  1. 解决死锁

    # 抓内存,看底层源码
    # 查看java运行的程序
    jps -l 
    
    2833 com.csh.thread.CallableDemo
    3393 sun.tools.jps.Jps
    1890 st
    691 
    3323 org.jetbrains.jps.cmdline.Launcher
    3324 com.csh.thread.DeadLockDemo
    
    # 分析线程情况
    jstack 进程ID
    
    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
            at com.csh.thread.HoldLockThread.run(DeadLockDemo.java:26)
            - waiting to lock <0x000000076ac19858> (a java.lang.String)
            - locked <0x000000076ac19890> (a java.lang.String)
            at java.lang.Thread.run(Thread.java:748)
    "Thread-0":
            at com.csh.thread.HoldLockThread.run(DeadLockDemo.java:26)
            - waiting to lock <0x000000076ac19890> (a java.lang.String)
            - locked <0x000000076ac19858> (a java.lang.String)
            at java.lang.Thread.run(Thread.java:748)
    
    Found 1 deadlock.
    
image-20220209163006365 image-20220209163132022
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值