可以以new Thread(()->{线程执行任务}).start();这种方式开启一个线程。当run()运行结束,线程会被GC释放。
在真实环境中,可能需要很多线程来支撑整个应用,当线程数量非常多时,反而会耗尽CPU资源
多线程可以最大力度的发挥多核cpu的计算能力,提高系统的吞吐量。如果我们不对线程加以管理与控制,反而会影响程序性能。线程的开销主要包括:
1、创建与启动线程的开销,创建线程还需要额外的分配栈空间,启动会有线程调度开销;
2、线程销毁的开销
3、线程调度的开销;上下文切换,通过系统的线程调度器调度线程
4、线程总数受限于cpu内核总数
线程池就是有效使用线程的一种常用方式:线程池内部可以预先创建一定数量的工作线程,客户端 代码直接将任务作为 一个对象提交给线程池,线程池将这些任务缓存在工作(阻塞)队列中,线程池的工作线程不断的从阻塞队列中取出任务并执行。
JDK提供了一套Exceutor框架,能帮助开发人员
java.util.concurrent Interface Executuor(基本的线程池接口)
方法: void execut(Runnable command) //线程池提交任务
子接口ExecutuorService 继承 Executuor
void shutdown() //关闭线程池-》不再接收新的任务,线程池中已接收的任务是正常执行完毕
submit(Runnable command)
实现类 ThreadPoolExecutor 实现ExecutuorService
子接口 ScheduledExcetuorService 继承 ExecutuorService
ScheduledFuture
实现类 ScheduledThreadPoolExecutor 实现ScheduledExcetuorService 并继承了
ThreadPoolExecutor
类Executors 提供若干工厂方法 依赖 ExecutuorService
//创建有5个线程大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(5);
//向线程池中提交15个任务
for(int i = 0;i<15;i++){
threadPool.execute(new Runnable(){
@Override
public void run(){
try{
Thread.sleep(3000);//模拟任务执行时长
}catch(InterruptedException e){
e.printStackTrace();
}
});
}
//创建一个有调度功能的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//在延迟 2 秒后执行任务, schedule( Runnable 任务, 延迟时长, 时间单位)
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "System.currentTimeMillis());
}
}, 2, TimeUnit.SECONDS);
//以固定的频率执行任务,开启任务的时间是固定的, 在 3 秒后执行任务,以后每隔 5秒重新执行一次
/* scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "----在固定频率开启任务--" + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3); //睡眠模拟任务执行时间 ,如果任务执行时长超过了时间间隔,则任务完成后立即开启下个任务
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 3, 2, TimeUnit.SECONDS);*/
//在上次任务结束后,在固定延迟后再次执行该任务,不管执行任务耗时多长,总是在任务结束
后的 2 秒再次开启新的任务
scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "----在固定频率开启任务---" + System.currentTimeMillis());
try {
TimeUnit.SECONDS.sleep(3); //睡眠模拟任务执行时间 ,如果任务执行时长超过了时间间隔,则任务完成后立即开启下个任务
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 3, 2, TimeUnit.SECONDS);
查看 Executors工具类中newCachedThreadPool(),newSingleThreadPool(),newFixedTreadPool()y源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new
SynchronousQueue());
}
/**该线程池在极端情况下,每次提交新的任务都会创建新的线程执行. 适合用来执行大量
耗时短并且提交频繁的任务
**/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue(),threadFactory);
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS, new
LinkedBlockingQueue()));
}
// 当不传ThreadFactory 参数时,threadFactory为 Executors.defaultThreadFactory()
//ThreadPoolExecutor 构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue
workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
底层都是使用ThreadPoolExecutor线程池,都是对ThreadPoolExecutor线程池的封装。
ThreadPoolExecutor的构造方法参数含义:
corePoolSize 指定线程池中核心线程的数量
maximumPoolSize 指定线程池中最大线程数量
keepAliveTime 当线程池中线程的数量超过corePoolSize时,多余的空闲线程的存活市场,即空闲线程在多长时长内销毁
unit 为keepAliveTime时长单位
workQueue 任务(阻塞)队列,把任务提交到该任务(阻塞)队列中等待执行
threadFactory 线程工厂,用于创建线程
handler 拒绝策略 当任务太多来不及处理时,如何拒绝
可以自定义线程池继承ThreadPoolExecutor,重写afterExecute和beforeExecute方法
ExecutorService executorService = new ThreadPoolExecutor(10,10,0,TimeUnit.SECONDS,new LinkedBlockingQueue<>()){//内部类
//重写BeforeExecute方法
//重写afterExecute方法
//重写 terminated方法 线程池退出时
}
ThreadPoolExecutor线程池提供了两个方法:
protected void afterExecute(Runnable r,Throwable t)//任务结束(任务异常退出)后执行
protected void beforeExecute(Thread t,Runnable r)//任务前执行
在ThreadPoolExecutor源码中定义了个内部类Worker,ThreadPoolExecutor线程池的工作线
程就是Worker类的实例,Worker类的实例在执行时也会调佣beforeExecute与afterExecute方法
一般来说,线程池大小需要考虑cpu数量,内存大小等因素
线程池大小 = CPU数量*目标cpu的使用率*(1+等待时间与计算时间比)
在使用 ThreadPoolExecutor 进行 submit 提交任务时,有的任务抛出了异常,但是线程池并没有
进行提示,即线程池把任务中的异常给吃掉了,可以把 submit 提交改为 execute 执行,也可以对
ThreadPoolExecutor线程池进行扩展.对提交的任务(submit 方法)进行包装