文章目录
项目开发中,不会使用之前3种线程的实现方式,因为之前3种线程实现方式无法控制线程,可能会造成系统资源耗尽,浪费系统资源,造成系统性能下降;
在企业业务开发中,必须使用线程池的方式,构建多线程;让线程充分利用,降低系统的资源的消耗。
问题:为什么要使用线程池???(池化技术)
我们需要执行一个java任务,可以直接new Thread 来运行任务,线程从创建到销毁经历哪些过程???
1.创建java线程实例,线程是一个对象实例,堆内存中分配内存(创建线程需要消耗时间和内存)
2.执行start方式启动线程,操作系统为Java线程创建对应的内核线程,线程处于就绪状态(内核线程是操作系统的资源,创建需要时间和内存)
3.线程被操作系统cpu调度器选中后,线程开始执行(run方法开始运行)
4.JVM开始为线程创建线程私有资源:JVM虚拟机栈*程序计数器(需要时间和内存)
5.线程运行过程中,cpu上下文切换(消耗时间,频繁切换,影响性能)
6.线程运行完毕,Java线程被垃圾回收器回收(销毁线程内存需要时间)
从线程执行的流程来看:
1.线程不仅是java对象,更是操作系统的资源(创建线程,消耗线程都需要时间)
2.Java线程的创建和运行都需要内存空间(线程数量太多,消耗很多内存)
3.cpu上下文切换(线程数量一大,cpu频繁切换)
线程池优势:
1.降低系统的资源的消耗
2.提高系统的响应速度
3.方便管理(线程复用,控制最大并发数,管理线程)
事先准备好一些资源,有人要用(业务系统要是有线程),就来我这里拿(线程池中获取),用完之后不能销毁,必须还给我(线程池线程可复用性)
package com.xd.cubemall.juc;import lombok.extern.slf4j.Slf4j;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;@Slf4j
public class ThreadPoolDemo {public static void main(String[] args) {// 1.创建线程池对象;创建单个线程的线程池对象ExecutorService executorService = Executors.newSingleThreadExecutor();//2.创建固定数量的线程池(指定核心线程数数量),核心线程数为2ExecutorService executorService2 = Executors.newFixedThreadPool(2);//3.创建一个按照计划执行的线程池ScheduledExecutorService executorService3 = Executors.newScheduledThreadPool(2);//4. 创建一个自动增长的线程池ExecutorService executorService4 = Executors.newCachedThreadPool();// 线程执行try {for (int i = 0; i < 10; i++) {executorService4.execute(()->{log.info("Executors创建线程池的方式实现多线程......");//业务代码执行int j = 100/3;log.info("业务代码执行结果:{}",j);});}} catch (Exception e) {e.printStackTrace();} finally {//线程池用完,关闭线程池executorService4.shutdown();}}
}
参数解析:
1.corePoolSize:线程池核心线程数,初始化线程池时候,会创建核心线程等待状态,核心线程不会被摧毁,提供线程的复用;
2.maximumPoolSize:最大线程数;核心线程用完了,必须新建线程执行任务,但是新建的线程不能超过最大线程数;
3.keepAliveTime: 线程存活时间,除了核心线程以外(maxinumPoolSize-corePoolSize)的线程存活时间;当线程处于空闲状态,他可以活多久;
4.unit:存活时间单位
5.workQueue:任务阻塞队列,任务可能会很多,线程就那么几个,因此可以把多余的任务放入队列进行缓冲,队列采用FIFO的,等待线程空闲,再从队列取出任务执行;
不建议使用以上的创建线程池的方式:
原因是以上的线程池创建的方式,当线程量一大后,可能造成无限制创建线程,从而导致内存被占满,线程量大导致性能严重下降,甚至OOM;
解决方案:
使用ThreadPoolExecutor类创建线程池;
// 可伸缩ThreadPoolExecutor threadPool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),9,3,TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {}
threadFactory: 线程工厂,默认使用defaultthreadFactory,用来创建线程的,一般使用默认即可;
RejectedExecutionHandler: 线程池拒绝策略
四种拒绝策略:
1.new ThreadPoolExecutor.AbortPolicy():新任务直接被拒绝,抛出异常:RejectedExecutionException;
2.DisCardPolicy:队列满了,新任务忽略不执行,直接丢弃,不会抛出异常
3.DisCardOldestPolicy:队列满了,新任务尝试和等待最久的线程竞争,也不会抛出异常;抛弃任务队列中等待最久任务,新任务直接添加到队列中
4.CallerRunPolicy:新任务来临后,直接使用调用者所在线程执行任务即可
合理配置线程相关的参数:核心线程数,最大线程数
设置线程池线程数的数量:根据业务类型进行设置(cpu密集型,io密集型)
如果是CPU密集型任务(所有任务都在内存中执行:没有磁盘的读写);建议线程池最大数量设置为N(cpu核心数量)+1
如果是IO密集型任务(大量磁盘读写任务):如果有IO操作,cpu此时处于空闲状态,最大线程数应该设置:2N+1
最大线程数设置公式:
最大线程数 = (任务执行时间/任务cpu时间)*N