Java线程池

一、线程池

  • 什么是线程池:

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

  • 作用:

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

  • 优点:

1)降低资源消耗
重复利用已创建的线程,降低线程创建和销毁造成的消耗
2)提高响应速度
任务到达时,不需要创建线程,就能立即执行
3)提高线程的可管理性
线程是稀缺资源,无限创建会消耗系统资源,降低系统稳定性,使用线程池可以统一分配、调优、监控


二、线程池生命周期

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
  • ctl: 表示线程池的运行状态(runState)和线程池中有效线程的数量(workCount)
    使用了Integer类型来保存,高3位保存runState,低29位保存workerCount
    在这里插入图片描述
  • RUNNING: 能接受新提交的任务,也能处理阻塞队列中的任务
  • SHUTDOWN: 不再接受新提交的任务,可以继续处理阻塞队列中已保存的任务
    1)在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态
    2) finalize() 方法在执行过程中也会调用shutdown()方法进入该状态
  • STOP:不接受新的任务,也不处理阻塞队列中的任务,会中断正在处理任务的线程
    1)在线程池处于 RUNNING 或SHUTDOWN状态时,调用 shutdownNow()方法会使线程池进入到该状态
  • TIDYING:当所有的任务都已终止,workCount(有效线程数为0),线程池进入该状态后,调用terminated()方法进入TERMINATED状态
  • TERMINATED:在terminated()方法执行完进入该状态
    进入TERMINATED状态条件:
    1)线程池不是RUNNING、TIDYING、TERMINATED状态
    2)线程池是SHUTDOWN并且workerQueue为空
    3)workCount为0
    4)设置TIDYING成功

三、线程池的基本参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • corePoolSize:常驻核心线程数
  • maximumPoolSize:最大同时执行线程数,>=1
  • keepAliveTime:多余空闲线程存活时间,当线程池的数量超过poolSize,线程空闲时间达到keepAliveTime,空闲的线程会被销毁,直到剩余线程数量为corePoolSize
  • unit:keepAliveTime的单位
  • workQueue:任务队列,提交但尚未被执行的任务
  • threadFactory:创建新线程的线程工厂,默认即可
  • handler:拒绝策略,当线程队列满了,并且工作线程>=线程池最大线程数(corePoolSize>=maximumPoolSize)时,拒绝请求执行的runnable策略

四、实现方式

  • Execuotrs.newFixedThreadPool(int n):定长线程池
   public static ExecutorService newFixedThreadPool(int nThreads) {
       return new ThreadPoolExecutor(nThreads, nThreads,
                                     0L, TimeUnit.MILLISECONDS,
                                     new LinkedBlockingQueue<Runnable>());
   }
  • 特点:
    1)定长线程池,控制最大并发数,超出的线程会在队列中等待
    2)corePoolSize和maximumPoolSize是相等
    3)使用LinkedBlockQueue作为阻塞队列
  • Executors.newSingleThreadExecutor():单线程化线程池
   public static ExecutorService newSingleThreadExecutor() {
       return new FinalizableDelegatedExecutorService
           (new ThreadPoolExecutor(1, 1,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>()));
   }
  • 特点:
    1)用唯一的工作线程来执行任务
    2)corePoolSize和maximumPoolSize都为1
    3)使用LinkedBlockQueue作为阻塞队列(由链表组成的有界阻塞队列)
  • Executors.newCacheThreadPool():可缓存线程池
   public static ExecutorService newCachedThreadPool() {
       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                     60L, TimeUnit.SECONDS,
                                     new SynchronousQueue<Runnable>());
   }
  • 特点:
    1)如果线程池长度超过处理需要,回收空闲线程,如果不足,则创建新线程
    2)corePoolSize为0和maximumPoolSize为int的最大值
    3)使用SynchronousQueue作为阻塞队列(不存储元素的阻塞队列,每个put操作必须等待一个take操作,否则不能添加元素)
    4)来任务了就创建线程,线程空闲超过60s就销毁线程

五、workQueue

workQueue:等待队列,当线程池中的线程数量>=corePoolSize时,新提交的任务,会被封装为一个Worker对象放入等待队列,有以下处理方式:

  • LinkedBlockingQueue:无界队列

线程池中能够创建的最大线程数就是corePoolSize,maximumPoolSize不会起作用。适用于FixedThreadPool与SingleThreadExcutor。基于链表的阻塞队列,创建的线程数不会超过corePoolSizes(maximumPoolSize值与其一致),当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中。
按照FIFO原则对元素进行排序,吞吐量高于ArrayBlockingQueue

  • ArrayListBlockingQueue:有界队列

使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗(CPU使用率、操作系统资源的消耗、上下文环境切换的开销),但也使得线程池对线程的调度变得更困难。队列大小和最大池大小可能需要相互折衷。

  • 设置较大的队列容量和较小的线程池容量:
    优点:降低资源消耗
    缺点:降低线程处理任务的吞吐量
  • 设置较小的队列容量和较大的线程池容量:
    优点:CPU使用率会提高
    缺点:当设置的线程池数量较大,同时提交的任务数较多,线程调度变困难,反而可能降低处理任务的吞吐量
  • SynchronousQueue:直接提交策略

适用于CachedThreadPool。它将任务直接提交给线程而不保持它们。如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程


六、拒绝策略
以下内置策略均实现了RejectExecutionHandler接口

  • ThreadPoolExcutor.AbortPolicy():直接抛出异常,默认策略,阻止系统正常运行
  • ThreadPoolExcutor.CallerRunsPolicy():"调用者运行"一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是让调用者所在的线程执行任务
  • ThreadPoolExcutor.DiscardOldersPolicy():抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交
  • ThreadPoolExcutor.DiscardPolicy():不处理,直接丢弃

七、为什么要手动创建线程池?
参考阿里巴巴java开发手册

【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM


八、如何合理配置线程池参数

  • CPU密集型:任务需要大量运算,没有阻塞
    线程池数量建议为:CPU核数+1
  • CPU密集型:任务需要大量I/O,频繁阻塞,大量线程被阻塞,所以需要多配置线程数
    线程池数量建议为:CPU核数/(1-阻塞系数),阻塞系数一般为0.8~0.9
    或者线程数为cpu核数*2

九、死锁编码及定位分析
jps定位线程id
jstack 线程id,查看死锁异常信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值