java多线程学习笔记 --六.线程池Executor

线程池Executor

new Thread弊端

  • 每次new新建对象,性能差
  • 线程缺乏统一的管理,可能会无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM
  • 缺少更多的功能,如更多的执行,定期执行,线程中断

线程池的好处

  • 重用存在的线程,减少对象创建 消亡的开销,性能佳
  • 可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
  • 提供定时执行,定期执行,单线程,并发数控制等功能

线程池的核心类—ThreadPoolExecutor

ThreadPoolExecutor构造函数的核心参数

  • corePoolSize:核心线程数量
  • maximumPoolSize:线程最大线程数
  • workQueue:阻塞队列。存储等待执行的任务,很重要,会对线程池运行过程产生重大影向
  • keepAliveTime:线程没有任务执行时最多保持多久时间终止
  • unit:keepAliveTime的时间单位
  • threadFactory:线程工厂,用创建线程
  • rejectHandler:当拒绝处理任务时的策略

生命周期图解

java多线程学习笔记 --六.线程池Executor

线程池的常用方法

  • execute():提交任务,交给线程池执行
  • submit():提交任务,能够返回执行结果 execute+Future
  • shutdown();关闭线程池,等待任务都执行完
  • shutdownNow():关闭线程池,不等待任务执行完
  • getTaskCount():线程池已执行和未执行的任务总数
  • getCompletedTaskCount():已完成的任务数量
  • getPoolSize():线程池当前的线程数量
  • getActiveCount():当前线程池中正在执行 任务的线程数量

线程池的类图

java多线程学习笔记 --六.线程池Executor

线程池—Executors

  • Executors.newCachedThreadPool

    创建一个可缓存的线程池,如果线程池的长度超过了处理的需要,可以灵活回收空闲线程。如果没有可回收的就新建线程

  • Executors.newFixedThreadPool创建一个定长的线程

  • Executors.newScheduledThreadPool

    定长线程池,支持定时和周期任务执行

  • Executors.newSingleThreadExecutor

    单线程化的线程池,用唯一的一个共用线程执行任务,保证所有任务按指定顺序执行(FIFO、优先级…)

使用请看代码

线程池—合理配置

  • CPU密集型任务,就需要尽量压榨CPU,参考值可以设为NCPU+1
  • IO密集型任务,参考值可以设置为2*CPU

死锁

概念

通俗的说,死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重影响。

用来描述死锁的问题最有名的场景就是“哲学家就餐问题”。哲学家就餐问题可以这样表述:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事之一:吃饭或者思考。吃东西的时候他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为只用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐。哲学家从来不交谈,这就跟危险,可能产生死锁,每个哲学家都拿着左手的餐叉永远等右边的餐叉(或者相反)…

死锁产生的必要条件

  • 互斥条件:进程对锁分配的资源进行排他性使用
  • 请求和保持条件:线程已经保持了一个资源,但是又提出了其他请求,而该资源已被其他线程占用
  • 不剥夺条件:在使用时不能被剥夺,只能自己用完释放
  • 环路等待条件:资源调用是一个环形的链

代码演示

详情请看IDEA的代码演示

Java项目中为了避免死锁需要使用log记录

请点击查看具体内容