Java进阶13 线程池
一、线程生命周期
线程被创建并启动以后,它并不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态,这各种状态就是线程的生命周期。
Java中的线程状态被定义在了java.lang.Thread.State枚举类
状态 | 含义 |
---|---|
NEW(新建) | 创建线程对象 |
RUNNABLE(就绪) | start方法被调用,但是还没有抢到CPU执行权 |
BLOCKED(阻塞) | 线程开始运行,但是没有获取到锁对象 |
WAITING(等待) | wait方法 |
TIMED-WAITING(计时等待) | sleep方法 |
TERMINATED(结束状态) | 代码全部运行完毕 |
1、线程生命周期图
过程:创建(NEW)线程后调用start方法进入就绪(RUNNABLE)状态,如果该线程顺利抢到CPU执行权,就进入运行状态,运行主要执行run方法中的代码逻辑,如果运行期间没有被其他线程夺走CPU执行权,则该线程将一直运行至结束,结束后进入死亡(TERMINTATED)状态。但如果运行期间被其他线程抢走CPU执行权,该线程将回到就绪状态。此外,当其运行期间调用了sleep()方法,则会进入即计时等待(TIMED-WAITING)状态 ,指定的休眠时间到达后,自动转为就绪状态;如果调用了wait()方法,就会进入无限等待(WAITING)状态,需要其他线程调用notify()方法才能继续进入就绪状态;如果在运行期间无法获取到锁,就会进入阻塞(BLOCKED)状态,知道其他线程释放了锁,该线程才能获得锁,获得锁之后就会进入就绪状态。
二、线程池
将线程对象交给线程池维护,可以降低系统成本,从而提升程序的性能
2、使用JDK提供的线程池
方法 | 说明 |
---|---|
static ExecutorService newCachedThreadPool() | 创建一个默认的线程池 |
static newFixedThreadPool(int nThreads) | 创建一个指定最多线程数量的线程池 |
3、自定义线程池
ThreadPoolExecutor类
3.1 构造方法
ThreadPoolExecutor(int corePoolSize,int maxnumPoolSize,long keepActiveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
参数1:核心线程数量【不能小于0】
参数2:最大线程数量(核心线程数+临时线程数)【不能小于等于0】
参数3:空闲时间(数值)【不能小于0】
参数4:时间单位【时间单位】
参数5:任务队列【不能为null】
有限队列 new ArrayBlockingQueue<>(队列长度)
无限队列 new LinkedBlockingQueue<>()
参数6:线程对象任务工厂【不能为null】
参数7:拒绝策略【不能为null】
3.2 Demo
public class ThreadPoolDemo2 {
public static void main(String[] args) {
/*
指定核心线程数为2,最大线程数为5,空闲时间为60秒,有限队列,线程任务工厂为指定默认的工厂,拒绝策略为AbortPolicy的自定义线程池;
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2,
5,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
for (int i = 1; i <= 16; i++) {
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"提交了线程任务");
}
});
}
}
}
3.3 注意事项
-
Q:临时线程对象何时创建?
A:提交的线程任务数量 > 核心线程数 + 任务队列数量
-
Q:什么时候会触发拒绝策略?
A:提交的线程任务数量 > 最大线程数 + 任务队列数量
3.4 拒绝策略
策略选项 | 说明 |
---|---|
ThreadPoolExecutor.AbortPolicy | 丢弃任务并抛出RejectedExecutionException异常(默认) |
ThreadPoolExecutor.DiscardPolicy | 丢弃任务,但是不抛出异常,这是不推荐的做法 |
ThreadPoolExecutor.DiscardOldestPolicy | 抛弃队列中等待最久的任务,然后把当前任务加入队列中 |
ThreadPoolExecutor.CallerRunPolicy | 调用任务的run()方法,绕过线程池直接执行 |
三、单例设计模式
单例指单个实例,保证类的对象在内存中只有一份
1、使用场景
如果创建一个对象需要消耗的资源过多,比如I/O与数据库的连接,并且这个对象完全是可以复用的,我们就可以考虑将其设计为单例的对象
2、设计模式
2.1 饿汉式
class Single {
//1、私有构造方法,组织其他类创建本类对象
private Single(){
}
//在本类中创建自己这个类的对象
private static Single s = new Single();
/**
* 提供一个方法供外部获取本类单例对象
* @return 返回单例对象
*/
public static Single getInstance(){
return s;
}
}
2.2 懒汉式(延迟加载模式)
class Single { //1、私有构造方法,组织其他类创建本类对象 private Single(){ } //在本类中创建自己这个类的对象 private static Single s = new Single(); /** * 提供一个方法供外部获取本类单例对象 * @return 返回单例对象 */ public static Single getInstance(){ return s; } }
注意事项:双重检查锁是可以减少上锁次数,避免创建出多个对象造成效率浪费