烟台网站建设报价,校园网站建设与管理问题分析,wordpress recaptcha,次世代建模培训为什么要使用线程池 线程的生命周期#xff1a;运行、就绪、运行、阻塞、死亡 下面是一个简单的创建多线程的方法。注意#xff1a;工作中不可取。 创建线程的时候#xff0c;我们避不开线程的生命周期。上面的方法虽然可以创建多线程#xff0c;但是创建完成后#xff0c…为什么要使用线程池 线程的生命周期运行、就绪、运行、阻塞、死亡 下面是一个简单的创建多线程的方法。注意工作中不可取。 创建线程的时候我们避不开线程的生命周期。上面的方法虽然可以创建多线程但是创建完成后我们可能还需要进行销毁如果中间出现异常就可能会导致回收不了或者在线程里面又创建一个线程而线程切换也需要消耗时间和空间就会导致线程管理起来很困难。
为了解决找个问题我们参考一下阿里的做法通过线程池的方式来管理线程。当然如果你有其他更好的管理线程的方式也可以。 JDK 常用的线程池
线程池作为一种池化技术实现起来比较困难但是 JDK下面的 java.util.concurrent 包提供了几种类型的线程池主要通过 Executors 类中的静态工厂方法来创建。
下面简单列出两种线程池的使用示例
public static void main(String[] args) {System.out.println(--- 创建唯一的线程 ---);ExecutorService executorService01 Executors.newSingleThreadExecutor();executorService01.execute(() - {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() : i);try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(--- Fixed 线程数5---);ExecutorService executorService02 Executors.newFixedThreadPool(5);for (int t 0; t 10; t) {executorService02.execute(() - {for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() : i);try {Thread.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}}});}
}上面代码运行我们发现线程创建后不会自动被销毁而且线程可以被复用可以减少每次创建线程消耗的时间和资源的浪费。
但是我们看阿里的开发手册上不允许使用 Executors 来创建而需要使用 ThreadPoolExecutor 类来创建如下图 我们看一下刚刚使用的两种线程池的源码发现其实都是由 ThreadPoolExecutor 类创建的线程。 所以后续我们就可以使用 ThreadPoolExecutor 类来自定义线程池。
为什么阿里不允许使用 Executors 来创建线程池呢我们可以在文档上看到 我们按照手册上写的看一下源码。 我们看到它是使用阻塞式队列来存线程以链表的方式。链表理论上是没有容量限制的但是其实是有限制的最大的容量就是 Integer.MAX_VALUE23亿左右理论上这个queue 可以放 23亿左右因为没有限制长度所以可能造成这个 queue 里面存放很多线程从而造成 OOM 错误。 为了避免上面的问题我们一般自定义线程池。
自定义线程池
线程池结构图 如上图所示执行顺序为当提交任务数大于核心线程数时会优先将任务放到阻塞队列中。当阻塞队列饱和时会扩充线程池中的线程数直到达到最大线程数。当任务数超出最大线程数时就会触发线程池的拒绝策略。
核心参数
序列参数名含义1corePoolSize核心线程数2maximumPoolSize最大线程数必须大于核心线程数3keepAliveTime空闲线程的存活时间4Unit时间单位5workQueue用于存放任务的队列6threadFactory线程工厂、用来创建新线程7handler处理被拒绝的任务 核心线程数corePoolSize指线程池中保持活动状态的最少线程数即使在空闲时也不会被回收。当有新的任务提交时如果当前活动线程数小于核心线程数则会创建新的线程来处理任务。 最大线程数maximumPoolSize线程池中允许存在的最大线程数当任务队列已满且当前活动线程数小于最大线程数时线程池会创建新的线程直到达到最大线程数。 空闲线程存活时间keepAliveTime指空闲线程在被回收之前可以等待新任务的最长时间。当线程池中的线程数量超过核心线程数并且处于空闲状态时这些多余的线程在超过指定时间后会被回收销毁。 时间单位unit空闲线程存活时间的单位可以是秒、毫秒、分钟等。 任务队列workQueue用于存放等待执行任务的阻塞队列。可以选择不同的队列类型来实现不同的调度策略。当线程池中的线程都在工作且任务队列已满时新的任务会被拒绝执行。 线程工厂threadFactory用于创建新线程的工厂可以自定义线程的名称、优先级等属性。 拒绝策略rejectedExecutionHandler当线程池已满并且任务无法执行时的处理策略如何处理新提交的任务。
拒绝策略 拒绝策略在线程池已满且无法接受新的任务时会被触发 线程池的线程数量已经达到了最大线程数无法再创建新的线程来执行任务。线程池中的任务队列已满无法接受新的任务。 当同时满足上面两个条件时拒绝策略会被触发根据所选的拒绝策略进行相应的处理。
需要注意的是拒绝策略的触发并不代表任务一定会被丢弃或忽视而是指当线程池已达到最大容量且任务队列已满时新提交的任务无法被正常处理因此需要通过拒绝策略来决定如何处理这些无法接受的任务。 AbortPolicy 中止策略 默认的拒绝策略当线程池已满且任务队列也已满时会抛出 RejectedExecutionException 异常来拒绝新提交的任务。 CallerRunsPolicy (调用者运行策略 当线程池已满且任务队列也已满时新提交的任务会由提交任务的线程来执行调用线程自己执行而不会开启新的线程。 DiscardPolicy (丢弃策略) 当线程池已满且任务队列也已满时直接丢弃新提交的任务不做任何处理。 DiscardOldestPolicy (丢弃最老策略) 当线程池已满且任务队列也已满时丢弃最早提交的任务然后尝试执行新提交的任务。
自定义线程池示例代码
import java.util.concurrent.*;public class CustomerThreadPool {static class MyAbortPolicy implements RejectedExecutionHandler {/*** Creates an {code AbortPolicy}.*/public MyAbortPolicy() { }/*** Always throws RejectedExecutionException.** param r the runnable task requested to be executed* param e the executor attempting to execute this task* throws RejectedExecutionException always*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException(任务 r.toString() 拒绝 from e.toString());}}public static void main(String[] args){int corePoolSize 4;int maximumPoolSize 10;long keepAliveTime 500;TimeUnit unit TimeUnit.MILLISECONDS;BlockingQueueRunnable workQueue new LinkedBlockingQueue(10);RejectedExecutionHandler handler null;ThreadPoolExecutor myPool new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,r-{System.out.println(创建线程:r);return new Thread(r);},new MyAbortPolicy());for (int t0;t20;t) {myPool.execute(()-{for (int i0;i10;i) {System.out.println(Thread.currentThread().getName():i);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}});}}
}