线程池

为什么使用线程池

  • 降低资源消耗,不用反复创建
  • 提高相应速度,不用立即创建
  • 提高线程管理性,进行统一分配,调度

主要特点

  1. 线程复用
  2. 控制最大并发数
  3. 管理线程

类似spring的生命周期
java中的string常量池

线程池如何使用

image

Java中的线程池通过Executor框架实现的,该框架用到了Executor、Executors(辅助工具类)、ExecutorService、ThreadPoolExecutor

newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。


    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newSingleThreadExecutor


    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

newCachedThreadPool


    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

newScheduleThreadPool



   public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

其实底层都是ThreadPoolExecutor,只是参数不同和阻塞队列不同导致不同的使用方法。

线程池的7大重要参数

corePoolSize

核心线程数

maximumPoolSize

最大线程数,一般大于等于核心线程数

workQueue

任务队列

keepAliveTime

线程存活时间

unit

存活时间单位

threadFactory

创建线程的工程

handler

拒绝策略

拒绝策略

  • AbortPolicy:直接抛异常(默认)
  • DiscardOldestPolicy:丢弃最前面的任务,然后重新尝此次任务(重复)
  • DiscardPolicy:丢弃任务,不抛异常
  • CallerRunsPolicy:优调度线程处理此任务,回到调用者去处理

线程池底层工作原理

image

  1. 创建线程池,等待提交过来的任务请求;
  2. 调用execute()方法添加一个请求任务时,会有一下逻辑
    • 如果正在运行的线程数小于core,立马创建
    • 如果大于core,将任务放入队列
    • 如果队列满了,且小于max,创建非核心线程数立即运行任务
    • 如果队列满了,且超过最大max,执行拒绝策略
  3. 一个线程完成任务,会从队列中取下一个任务来执行。
  4. 当一个线程无事可做,超过keepAliveTime,线程池会判断,如果当前运行的线程数大于core,这个线程会被停掉。所有线程最终完成任务时,会收缩到core的大小。

工作中你用到哪个?

java提供的都不要用,用自定义参数的线程池!!

阿里巴巴开发手册规定:

fixed和single 会创建Integer.MAX_VALUE的队列 oom

cached和schedule会创建Integer.MAX_VALUE的线程 oom

手写线程池

  后续结合配置中心 根据数据量 做线程池的动态参数调整
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
        try {

            for (int i = 1; i <= 9; i++) {
                threadPoolExecutor.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "办理业务");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
        
        
pool-1-thread-1办理业务
pool-1-thread-4办理业务
main办理业务
pool-1-thread-3办理业务
pool-1-thread-2办理业务
pool-1-thread-3办理业务
pool-1-thread-4办理业务
pool-1-thread-5办理业务
pool-1-thread-1办理业务

线程池配置合理参数

CPU密集型

IO密集型

线程数=CPU核数/1=(0.8~0.9)

死锁编码及定位分析

死锁原因

  • 俩个以上的线程,抢夺资源互相等待的一种现象。

死锁代码以及分析


package com.example.thread;


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);

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+":自己有"+lockB+"想获得"+lockA);
            }
        }

    }
}



/**
 * @author qiumeng
 * @version 1.0
 * @description
 * @date 2020/8/23 10:46
 */
public class DeadLocKDemo {

    public static void main(String[] args) {

        String lockA="lockA";
        String lockB="lockB";

         new Thread(new HoldLocKThread(lockA,lockB),"aaaaa").start();
         new Thread(new HoldLocKThread(lockB,lockA),"bbbbbb").start();
    }
}

解决方案

  1. jsp -l
10784 com.example.thread.DeadLocKDemo
11312
9696 org.jetbrains.jps.cmdline.Launcher
8248 sun.tools.jps.Jps
3724 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
9596 org.jetbrains.idea.maven.server.RemoteMavenServer
  1. jstack 进程id
"bbbbbb":
        at com.example.thread.HoldLocKThread.run(DeadLocKDemo.java:20)
        - waiting to lock <0x000000076b145340> (a java.lang.String)
        - locked <0x000000076b145378> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)
"aaaaa":
        at com.example.thread.HoldLocKThread.run(DeadLocKDemo.java:20)
        - waiting to lock <0x000000076b145378> (a java.lang.String)
        - locked <0x000000076b145340> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值