java多线程学习笔记 --六.线程池Executor
线程池Executor
new Thread弊端
- 每次new新建对象,性能差
- 线程缺乏统一的管理,可能会无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM
- 缺少更多的功能,如更多的执行,定期执行,线程中断
线程池的好处
- 重用存在的线程,减少对象创建 消亡的开销,性能佳
- 可以有效的控制最大并发线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞
- 提供定时执行,定期执行,单线程,并发数控制等功能
线程池的核心类—ThreadPoolExecutor
ThreadPoolExecutor构造函数的核心参数
- corePoolSize:核心线程数量
- maximumPoolSize:线程最大线程数
- workQueue:阻塞队列。存储等待执行的任务,很重要,会对线程池运行过程产生重大影向
- keepAliveTime:线程没有任务执行时最多保持多久时间终止
- unit:keepAliveTime的时间单位
- threadFactory:线程工厂,用创建线程
- rejectHandler:当拒绝处理任务时的策略
生命周期图解
线程池的常用方法
- execute():提交任务,交给线程池执行
- submit():提交任务,能够返回执行结果 execute+Future
- shutdown();关闭线程池,等待任务都执行完
- shutdownNow():关闭线程池,不等待任务执行完
- getTaskCount():线程池已执行和未执行的任务总数
- getCompletedTaskCount():已完成的任务数量
- getPoolSize():线程池当前的线程数量
- getActiveCount():当前线程池中正在执行 任务的线程数量
线程池的类图
线程池—Executors
-
Executors.newCachedThreadPool
创建一个可缓存的线程池,如果线程池的长度超过了处理的需要,可以灵活回收空闲线程。如果没有可回收的就新建线程
-
Executors.newFixedThreadPool创建一个定长的线程
-
Executors.newScheduledThreadPool
定长线程池,支持定时和周期任务执行
-
Executors.newSingleThreadExecutor
单线程化的线程池,用唯一的一个共用线程执行任务,保证所有任务按指定顺序执行(FIFO、优先级…)
使用请看代码
线程池—合理配置
- CPU密集型任务,就需要尽量压榨CPU,参考值可以设为NCPU+1
- IO密集型任务,参考值可以设置为2*CPU
死锁
概念
通俗的说,死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重影响。
用来描述死锁的问题最有名的场景就是“哲学家就餐问题”。哲学家就餐问题可以这样表述:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事之一:吃饭或者思考。吃东西的时候他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为只用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐。哲学家从来不交谈,这就跟危险,可能产生死锁,每个哲学家都拿着左手的餐叉永远等右边的餐叉(或者相反)…
死锁产生的必要条件
- 互斥条件:进程对锁分配的资源进行排他性使用
- 请求和保持条件:线程已经保持了一个资源,但是又提出了其他请求,而该资源已被其他线程占用
- 不剥夺条件:在使用时不能被剥夺,只能自己用完释放
- 环路等待条件:资源调用是一个环形的链
代码演示
详情请看IDEA的代码演示