启动网站建设的请示,工程网站怎么做,一个app下载免费下载安装,杭州建设网公益广告宣传线程其实对于操作系统来说是宝贵的资源#xff0c;java层面的线程其实本质还是依赖于操作系统内核的线程进行处理任务#xff0c;如果频繁的创建、使用、销毁线程#xff0c;那么势必会非常浪费资源以及性能不高#xff0c;所以池化技术#xff08;数据库连接池、线程池java层面的线程其实本质还是依赖于操作系统内核的线程进行处理任务如果频繁的创建、使用、销毁线程那么势必会非常浪费资源以及性能不高所以池化技术数据库连接池、线程池在性能优化的时候是重中之重。
我们来猜想以下线程池的功能因为如果是一个线程一个线程执行任务那么我们需要进行对线程的管理、以及对于任务的分配在执行过程中本质还是利用多个线程通过从任务队列中获取任务进行执行。通过这种方式当任务来临时可以直接使用线程达到复用。
demo ThreadPoolExecutor pool new ThreadPoolExecutor(5, 10, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque(15),new ThreadFactory() {private final AtomicInteger atomicInteger new AtomicInteger(1);Overridepublic Thread newThread(Runnable r) {return new Thread(r, pool- atomicInteger.getAndIncrement());}}, new ThreadPoolExecutor.DiscardPolicy());//执行pool.execute(new Runnable() {Overridepublic void run() {System.out.println(qxlxi);}});//关闭pool.shutdown();boolean terminated false;while (!terminated) {pool.awaitTermination(100,TimeUnit.SECONDS);}System.out.println(pool is shutdowm.);
线程池的创建 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) corePoolSize 线程池中的常驻核心线程数 core: core : 缓冲队列超过缓冲队列就直接新建。核心线程不回销毁而非核心线程超过一定时间没有使用就会销毁。 maxmumPoolSize : 整个线程池的核心线程和非核心线程数 maxmumPoolSize - corePoolSize 就是非核心线程数。 keepAliveTime unit 非核心线程数销毁的时间可以自定义 workQueue 当有新的任务请求线程时超过核心线程数那么就会将任务先存储到任务队列中等待线程处理。是一个阻塞队列。 有Array、Linked、Priority、Synchron等。 handler : 当没有空闲线程进行处理任务的时候超过来最大线程数那么就需要执行线程池的拒绝策略。可以通过hanlder进行设置。只针对有届阻塞队列 可以看到通过一个抽象的接口然后实现不同的策略来进行执行拒绝策略当然我们也可以实现自己的策略拒绝类。
public interface RejectedExecutionHandler {void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}DiscardPolicy 什么也不做。
public static class DiscardPolicy implements RejectedExecutionHandler {public DiscardPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}}AbortPolicy 直接返回异常。不执行
public static class AbortPolicy implements RejectedExecutionHandler {public AbortPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException(Task r.toString() rejected from e.toString());}}CallerRunsPolicy 策略是 任务提交者来执行这个任务。
public static class CallerRunsPolicy implements RejectedExecutionHandler {public CallerRunsPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}}DiscardOldestPolicy 策略是判断线程是否关闭状态没有关闭的化直接删除workQueue中的一个任务然后将其加入其中。 public static class DiscardOldestPolicy implements RejectedExecutionHandler {public DiscardOldestPolicy() { }public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}}threadFactory 线程创建ThreadPoolExecutor对象时传入ThreadFactory工厂类对象那么线程池中的对象均会通过工厂类的new Thread()方法来实现。可以通过定义new Thread()对象来创建添加一些信息。当然我们还可以通过 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor等方式。
线程池的执行
线程池执行任务的时候只需要将执行的任务封装成Runnable对象然后将Runnable对象传递给execute()函数线程池在创建的时候并不会提前创建而是当有任务的时候才会创建。 1.核心线程是否已满没有满 直接创建 2.核心线程已满则检查等待队列是否已满未满将任务放入队列中 3.等待队列已满检查非核心线程非核心线程未满常见非核心线程 4.核心线程、等待队列、非核心线程都满了执行对应的拒绝策略 整体的流程其实是创建核心线程之后就会从wrokQueue中获取任务通过take()函数进行执行如果没有任务的化就会阻塞等待非核心线程创建之后会调用workQueue()的poll()不同从workQueue()获取任务poll()函数是阻塞函数根take()函数不同的时poll()函数可以设置阻塞的超时时间poll()的超时时间超过非核心线程的等待时间那么就会超时返回执行线程销毁。
关闭 public void shutdown() {final ReentrantLock mainLock this.mainLock;mainLock.lock();try {checkShutdownAccess();advanceRunState(SHUTDOWN);interruptIdleWorkers();onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}tryTerminate();}public ListRunnable shutdownNow() {ListRunnable tasks;final ReentrantLock mainLock this.mainLock;mainLock.lock();try {checkShutdownAccess();advanceRunState(STOP);interruptWorkers();tasks drainQueue();} finally {mainLock.unlock();}tryTerminate();return tasks;}线程池关闭有两个方法一种是shutdown() 以及shutdownNow()。前者是通过优雅的方式会执行完正在执行以及等待队列中的任务后者则是通过直接将处理中以及清空等待队列并向所有线程发送中断请求。shutdownNow的返回值是等得队列中未被执行的任务。需要注意的是返回时有可能线程池内还有线程在执行任务需要等所有线程都执行完毕之后调用awaitTermination函数阻塞等待。
配置
在实际的应用开发中我们如何进行合理配置线程池的大小呢一般通俗来说的化IO密集型和计算密集型计算密集型设置未CPU核心相当就可以IO密集型因为大部分时间都在IO阻塞上所以将线程池适当开大点。除此之外就是IO计算相结合的方式。
具体的方式其实就需要统计花在IO和计算上的占比pool_size * 核数 (cpu_time io_time) / cpu_time。 比如cpu_time 占用 1/3 io占用 2/3 那么就是3. 当然在实际的层面来说还需要考虑别的地方有没有瓶颈比如数据库连接池文件句柄等。也就是木桶效应。根据最短的进行合理评估在实际中就遇到DB 连接池配置失效当时吞吐量上不去最后发现后才解决。
小结
参数说明 corePoolSize指定了线程池中的线程数量。 maximumPoolSize指定了线程池中的最大线程数量。 keepAliveTime当前线程池数量超过corePoolSize时多余的空闲线程的存活时间即多次时间内会被销毁。CachedThreadPool是60秒。 unit: keepAliveTime的单位。 workQueue: blockQueue的配置。新加入任务的时候当线程池中的可用线程小于第一个参数core线程数量那么直接new一个线程或者用空闲的可用线程来执行任务。不用进行排队。当线程池中core线程数量都处于执行中那么就把任务加入到blockqueue中进行排队等待。当排队队列满了那么新new一个线程执行最新加入到queue中的任务。如果线程池中的线程数量超过了最大线程数量那么这个时候将拒绝新加入的任务。如果最大线程数都满了队列中也满了这个时候还有新任务请求进来那么会报错默认是抛出RejectedException。
blockqueue有三种策略 第一种是配置一个SynchronousQueue这种queue其实不是真正的queue他根本就不会进行排队如果core线程数量满了那么新来一个任务会直接new一个线程而不是进入排队直到池中的线程数量超过最大线程数开始拒绝新加入的任务JDK的CacheThreadLocal就是使用的这个工作队列配置的最大线程数是Integer.MAXVALUE。 第二种是配置一个LinkedBlockingQueue这种queue本身是没有大小的也就是说这种queue永远也满不了可以无限排这个时候最大线程数就没有意义了因为queue永远不满所以这种配置就相当于是一个固定的大小为core线程数的线程池JDK的FixedThreadPool就是采用的这个 第三种策略是用ArrayBlockingQueue我们可以给这个queue指定大小。比如200啊300啊那么只要queue大小满了就会产生新的线程来处理queue的头部任务。如果池中超过了最大线程数那么会拒绝任务的加入。
threadFactory线程工厂用于创建线程一般用默认的即可。如果默认的不能满足我们的要求我们可以使用自定义的线程工厂。 RejectedExceptionHandler拒绝策略当BlockQueue都满了无法接收新的任务了就会触发RejectedExceptionHandler的方法了这是一个策略模式的很好的例子。JDK提供了几种现成的拒绝策略默认的拒绝策略是AbortPolicy抛出RejectedException这是一个运行时的异常但是线程池执行器依旧可以继续工作再次提交新的任务的时候可能又会抛出RejectedException。CallerRunsPolicy这个策略是在调用者的线程中运行被抛弃的任务相当于在线程池submit任务的时候在调用者线程中执行runnable显然这个策略很糟糕DiscardoldestPolicy这个策略是将队列中最老的挤出去抛弃掉然后再次提交该任务DiscardPolicy直接丢弃无法处理的任务不做任何处理。一般来说默认的拒绝策略是最好的但是如果我们还想要自定义的拒绝策略我们可以自己实现一个RejectedExceptionHandler策略。