做网站主题,沈阳网站设计制作,达内教育口碑怎么样,正能量视频素材免费下载网站一.说一下线程池的执行原理
1.线程池的七大核心参数
#xff08;1#xff09;int corePoolSize#xff1a;核心线程数。默认情况下核心线程会一直存活#xff0c;当设置allowCoreThreadTimeout为true时#xff0c;核心线程也会被超时回收。
#xff08;2#xff09;i…一.说一下线程池的执行原理
1.线程池的七大核心参数
1int corePoolSize核心线程数。默认情况下核心线程会一直存活当设置allowCoreThreadTimeout为true时核心线程也会被超时回收。
2int maximumPoolSize线程池能容纳的最大线程数。maximumPoolSize 核心线程数 救急线程数。
3long keepAliveTime线程闲置的超时时长。当线程闲置超时时此线程会被回收释放资源。超时回收默认情况下只针对救急线程。
4TimeUnit unitkeepAliveTime的时间单位如TimeUnit.MILLISECONDS、TimeUnit.SECONDS等。
5BlockingQueueRunnable workQueue任务队列。当核心线程数都被占用时新提交的任务会被放到任务队列中等待。
6ThreadFactory threadFactory线程工厂为可选参数。 a.ThreadFactory为一个接口只提供一个newThread()方法传入一个Runnable对象返回一个执行该Runnable的Thread对象。用于定制线程对象的创建。 b.若未指定ThreadFactory则线程池会使用默认的ThreadFactory——只是创建Thread执行Runnable不对线程作额外的处理。
7RejectedExecutionHandler handler拒绝策略为可选参数 a.当核心线程、救急线程都被占用且任务队列已满时会对新提交的任务执行拒绝策略。 b.若未指定拒绝策略则线程池会默认使用AbortPolicy
2.线程池的拒绝策略
1AbortPolicy丢弃新任务并抛出异常。
2DiscardPolicy丢弃新任务但不会抛出异常。
3DiscardOldestPolicy丢弃任务队列队头的任务将新任务插入到任务队列队尾。
4CallerRunsPolicy让调用者线程自己来执行任务。即如果当前线程A向线程池提交了一个任务且该任务触发了线程池的CallerRunsPolicy拒绝策略则线程池会将该任务退回给线程A让线程A来执行该任务。
5自定义拒绝策略实现RejectedExecutionHandler接口并重写rejectedExecution()方法。
3.线程池的执行原理
对于新提交的任务
1判断核心线程是否有空闲若有空闲则交由核心线程执行。若核心线程都被占用则继续判断
2判断等待队列是否有空位若有空位则插入到队尾。若等待队列已满则继续判断
3判断救急线程是否有空闲若有空闲则交由救急线程执行。若救急线程都被占用则执行拒绝策略
补充
1核心线程和救急线程每执行完一个任务都会再去等待队列中取待执行任务继续执行。若等待队列中没有任务则默认情况下核心线程进入空闲状态救急线程开始闲置超时的计时。
2当核心线程都被占用且等待队列已满时才会创建救急线程来执行新任务。被创建的救急线程会直接忽视等待队列的任务先去执行新提交的任务等到新任务执行完才会开始执行等待队列的任务。
二.线程池中常见的任务队列
1.ArrayBlockingQueue基于数组结构的有界阻塞队列任务执行顺序为FIFO
2.LinkedBlockingQueue基于链表结构的有界阻塞队列任务执行顺序为FIFO
3.DelayedWorkQueue是一个优先级队列。在提交任务时可以指定当前任务需要延时多长时间后才执行DelayedWorkQueue的任务执行顺序为当前队列中指定执行时间最早的任务。
4.SynchronousQueue不存储元素的阻塞队列。每个线程的插入操作都需要等待另一个线程的移除操作是线程和线程间的传递。
三.说一下ArrayBlockingQueue和LinkedBlockingQueue的区别
1.底层数据结构
1ArrayBlockingQueue底层是数组
2LinkedBlockingQueue底层是单向链表
2.容量
1ArrayBlockingQueue是强制有界创建时需要传入一个int值作为容量
2LinkedBlockingQueue是默认无界支持有界。默认容量为Integer.MAX_VALUE创建时可以传入一个int值作为容量。
3.加锁方式
ArrayBlockingQueue和LinkedBlockingQueue都是使用ReentrantLock加锁。
1ArrayBlockingQueue的插入和删除操作都是只加一把锁同时锁住入队和出队。
2LinkedBlockingQueue插入操作锁尾节点不锁头节点删除操作锁头节点不锁尾节点。即在执行入队时可以出队执行出队时可以入队效率更高。
四.线程池的execute()和submit()方法的区别
线程池可以通过execute()或submit()方法来提交任务
1.执行对象不同
1execute(Runnable command)该方法用于执行Runnable接口的任务
2submit(CallableT task)、submit(Runnable task)、submit(Runnable task,T result)submit()的三个重载方法表明其既可以执行Callable接口任务也可以执行Runnable接口任务。传入的Runnable接口在内部会先被转化为Callable接口再作为任务执行因此submit()方法最终的执行对象都是Callable接口。
2.返回值不同
1execute()方法执行对象是Runnable接口任务Runnable的run()方法没有返回值因此execute()方法也没有返回值。execute()主要用于执行不需要返回结果的任务。
2submit()方法最终执行对象都是Callable接口任务其返回值为FutureT可以调用Future的get()方法获取任务执行后的返回值。
3.异常处理不同
1execute()提交的任务在执行中遇到异常时会将异常传递给线程的UncaughtExceptionHandler处理默认是直接将异常抛出。
2submit()提交的任务在执行中遇到异常时仅仅是停止执行任务但不会将异常立即抛出相当于暂时吞掉异常直到外部调用Future的get()方法尝试获取任务执行的返回值时才会将异常抛出。即允许延迟处理异常。
五.线程池有哪几种状态
1.RUNNING线程池的初态代表线程池正在处理和接收任务。
2.SHUTDOWN调用shutdown()方法会进入此状态。表明线程池不再接收新任务但会继续处理已经提交到等待队列的任务。
3.STOP调用shutdownNow()方法会进入此状态。表明线程池不再接收新任务也不会再处理等待队列中的任务同时还会尝试中断正在执行的任务。
4.TIDYING过渡态当线程池的所有任务都已经终止(无论是正常执行完还是通过取消或异常终止)并且所有工作线程都关闭就会进入此状态。该状态下线程池会执行terminated()钩子方法。
5.TERMINATED最终态。当terminated()方法执行完就会进入此状态代表线程池完全终止。
六.线程池的种类有哪些
Executors类提供了创建多种线程池的静态方法常见的有4种
1.newFixedThreadPool固定线程数的线程池。
1核心线程数最大线程数无救急线程。
2任务队列为LinkedBlockingQueue容量为默认的Integer.MAX_VALUE。
3适用于任务量已知相对耗时的任务。
2.newSingleThreadPool单线程化的线程池。
1核心线程数最大线程数1作为唯一的工作线程无救急线程。
2任务队列为LinkedBlockingQueue容量为默认的Integer.MAX_VALUE。
3适用于按照顺序执行的任务。
3.newCachedThreadPool可缓存线程
1核心线程数0最大线程数救急线程数Integer.MAX_VALUE即全为救急线程在工作因此可以灵活创建、回收线程。
2任务队列为SynchronousQueue
3适用于任务数比较密集但每个任务执行时间较短的情况。
4.newScheduledThreadPoolExecutor具有延迟和周期执行功能的线程池。
1核心线程数自定义最大线程数Integer.MAX_VALUE。
2任务队列为DelayedWorkQueue使用schedule()提交任务指定任务的延时执行时间。
七.为什么不建议使用Executors类提供的方法创建线程
1.FixedThreadPool和SignalThreadPool都允许任务队列的长度为Integer.MAX_VALUE可能会堆积大量的任务请求导致OOM。
2.newCachedThreadPool和newScheduledThreadPool的最大线程数都为Integer.MAX_VALUE可能会创建大量的线程导致OOM。
八.如何控制某个方法允许并发访问线程的数量
1.使用newFixedThreadPool但有OOM的风险。
2.使用Semaphore类每一个线程访问该方法都acquire()获取一个信号量若信号量数大于0则该线程获取到一个信号量信号量数减1若信号量数小于0则该线程阻塞若线程执行完该方法则release()释放一个信号量信号量数加1。
九.如何确定线程池的核心线程数
假设cpu的核数为N
1.对于高并发、执行时间短的任务需要减少线程数以减少线程上下文切换带来的消耗。因此核心线程数设置为N1。
2.对于并发不高、任务执行时间长的任务
1IO密集型任务例如文件读写、DB读写、网络请求等不需要占用过多cpu。核心线程数设置为2N1。
2cpu密集型任务例如计算型代码、Bitmap转换、Gson转换等需大量占用cpu需要减少线程数以减少线程上下文切换带来的消耗。因此核心线程数设置为N1。
十.说一下线程池的使用场景
1.数据批量导出
1使用线程池CountDownLatch批量将数据库中的数据导出避免OOM。
2场景例如使用分页查询去导出数据库中的数据线程池的每个线程负责一页数据的导出并发执行每有一个线程执行完CountDownLatch减一直到所有线程执行完CountDownLatch等于零主线程才继续执行。
2.数据汇总
1如果一个请求要调用多个接口来汇总数据且接口间没有依赖关系则可以使用线程池Future获取调用结果的方式来并发执行提升性能。
3.异步线程
1为了避免下一级方法影响上一级方法响应时间可以使用异步线程并发调用下一级方法(前提是不需要下一级方法的返回值)提高响应速率。