JDK1.5中的线程池使用简介
- 格式:doc
- 大小:34.00 KB
- 文档页数:3
线程池的运行流程线程池是一种用来管理和复用线程的技术,它可以有效地提高多线程程序的性能和资源利用率。
线程池通过预先创建一定数量的线程,并将任务分配给这些线程来处理,避免了不断创建和销毁线程的开销。
线程池的运行流程如下:1. 线程池的创建:线程池的创建需要确定线程池的大小、任务队列的长度、线程的优先级等参数。
可以通过ThreadPoolExecutor类创建线程池,其中ThreadPoolExecutor构造函数的参数可以指定线程池的大小、任务队列的长度等。
2.初始化线程池:创建线程池之后,线程池会创建一定数量的线程,这些线程处于等待状态,等待任务的到来。
线程池还会创建一个任务队列,用来存放待执行的任务。
3. 提交任务:当需要执行任务时,可以通过execute方法提交任务到线程池。
线程池会将任务放入任务队列中,等待线程来执行。
如果任务队列已满,线程池会根据配置的策略进行处理,比如直接丢弃任务、抛出异常等。
4.选择线程执行任务:线程池会从任务队列中选择一个任务,然后从线程池中选择一个空闲的线程来执行任务。
线程池可以根据多种策略来选择线程,比如先进先出、最近未使用等。
5.执行任务:选中线程开始执行任务。
线程执行任务期间,会不断从任务队列中获取任务并执行,直到任务队列为空或线程池被关闭。
6.线程复用:任务执行完毕之后,线程不会被销毁,而是回到线程池中,等待新的任务。
这样可以减少线程的创建和销毁的开销,提高了线程的复用率。
7.监控和管理:线程池通常会提供一些监控和管理的功能,比如可以监控线程池的运行状态、线程的活动数、任务队列的长度等,还可以根据需求进行动态调整线程池的大小、任务队列的长度等。
8. 关闭线程池:当不再需要线程池时,可以调用shutdown方法来关闭线程池。
关闭线程池后,线程池不再接受新的任务,但会继续执行已提交的任务。
可以通过awaitTermination方法等待线程池中所有任务执行完毕,然后关闭线程池。
java线程池的使用例子随着计算机技术的不断发展,我们的软件系统越来越复杂,程序的性能要求也越来越高。
在这样的背景下,线程池成为了一种非常重要的工具。
Java线程池是Java提供的一种简单易用的线程管理工具,可以帮助我们更好地管理程序中的线程,提高程序的性能和稳定性。
本文将通过一个实际的例子来介绍Java线程池的使用方法和注意事项。
希望读者可以通过本文的学习,更好地掌握Java线程池的使用技巧。
一、什么是线程池?在介绍Java线程池之前,我们需要先了解什么是线程池。
线程池是一种管理线程的机制,可以帮助我们更好地管理程序中的线程,提高程序的性能和稳定性。
线程池的主要作用是为每个任务分配一个线程,当任务完成后,线程会被回收并可供下一个任务使用。
这样,线程的创建和销毁的开销就可以得到控制,避免了频繁创建和销毁线程所带来的性能损失。
二、Java线程池的使用方法1. 创建线程池Java线程池的创建方式非常简单,只需要使用ThreadPoolExecutor类即可。
以下是一个简单的线程池创建代码: ```ExecutorService executor =Executors.newFixedThreadPool(5);```这个代码创建了一个固定大小为5的线程池。
如果需要创建其他类型的线程池,可以使用其他的静态工厂方法,如newCachedThreadPool()、newSingleThreadExecutor()等。
2. 提交任务创建好线程池之后,我们就可以向线程池提交任务了。
以下是一个简单的线程池提交任务代码:```executor.submit(new Runnable() {@Overridepublic void run() {// 执行任务}});```这个代码提交了一个Runnable类型的任务,线程池会自动为其分配一个线程执行。
如果需要提交其他类型的任务,可以使用Callable、Future等接口。
java中实现并发的方法Java是一种面向对象的编程语言,它在并发编程方面提供了多种实现方法。
并发编程指的是同时执行多个任务的能力,这在处理大量数据或高负载时非常重要。
本文将介绍Java中实现并发的几种常用方法。
1. 线程(Thread)线程是Java中最基本的并发编程方法。
通过创建多个线程,可以实现并行执行多个任务。
在Java中,可以通过两种方式创建线程:继承Thread类或实现Runnable接口。
继承Thread类需要重写run()方法,而实现Runnable接口需要实现run()方法。
通过调用start()方法启动线程,线程将在自己的独立执行路径上执行任务。
2. 线程池(ThreadPoolExecutor)线程池是一种管理和复用线程的机制,可以避免频繁创建和销毁线程的开销。
Java提供了ThreadPoolExecutor类来实现线程池。
通过创建一个线程池,可以将任务提交给线程池,线程池会自动分配线程来执行任务。
线程池还可以控制并发线程的数量,避免系统资源被过度占用。
3. Callable和FutureCallable是一个带有返回值的任务,与Runnable接口类似,但它可以返回执行结果。
Java提供了Future接口来表示异步计算的结果。
通过调用submit()方法提交Callable任务给线程池,将返回一个Future对象,可以使用该对象获取任务的执行结果。
4. 并发集合(Concurrent Collections)Java提供了一些并发安全的集合类,例如ConcurrentHashMap、ConcurrentLinkedQueue等。
这些集合类在多线程环境下使用时,可以避免出现线程安全问题。
并发集合类采用了一些特殊的数据结构和算法来保证线程安全性,能够高效地处理并发访问。
5. 锁(Lock)锁是一种同步机制,可以保证多个线程对共享资源的互斥访问。
Java提供了synchronized关键字来实现锁机制,也提供了Lock接口及其实现类来实现更加灵活的锁。
线程池注意事项和常见问题线程池是一种常用的多线程编程技术,它可以提高程序的性能和稳定性。
但是,在使用线程池时,我们也需要注意一些问题和常见错误。
本文将介绍线程池注意事项和常见问题,帮助读者更好地使用线程池。
一、线程池注意事项1.线程池大小的选择线程池大小的选择需要根据实际情况进行调整。
如果线程池过小,可能会导致任务无法及时处理,从而影响程序的性能;如果线程池过大,可能会导致系统资源的浪费,从而影响程序的稳定性。
2.任务队列的选择任务队列的选择也需要根据实际情况进行调整。
如果任务队列过小,可能会导致任务无法及时处理,从而影响程序的性能;如果任务队列过大,可能会导致系统资源的浪费,从而影响程序的稳定性。
3.线程池的关闭线程池的关闭需要注意线程池中的任务是否已经全部完成。
如果线程池中还有未完成的任务,直接关闭线程池可能会导致任务丢失或者程序异常。
因此,在关闭线程池之前,需要等待所有任务都已经完成。
二、线程池常见问题1.线程池中的任务出现异常线程池中的任务出现异常可能会导致整个线程池崩溃。
因此,在编写任务时,需要注意异常处理,避免出现未处理的异常。
2.线程池中的任务阻塞线程池中的任务阻塞可能会导致线程池无法及时处理其他任务,从而影响程序的性能。
因此,在编写任务时,需要注意任务的执行时间,避免出现长时间阻塞的情况。
3.线程池中的任务过多线程池中的任务过多可能会导致系统资源的浪费,从而影响程序的稳定性。
因此,在使用线程池时,需要根据实际情况进行调整,避免出现任务过多的情况。
4.线程池中的线程过多线程池中的线程过多可能会导致系统资源的浪费,从而影响程序的稳定性。
因此,在使用线程池时,需要根据实际情况进行调整,避免出现线程过多的情况。
总之,线程池是一种常用的多线程编程技术,但是,在使用线程池时,我们也需要注意一些问题和常见错误。
只有正确地使用线程池,才能提高程序的性能和稳定性。
详解什么是Java线程池的拒绝策略?⽬录⼀、拒绝策略1.1AbortPolicy(默认拒绝策略)1.2CallerRunsPolicy(使⽤调⽤线程池的线程来执⾏任务)1.3DiscardPolicy(忽略新任务)1.4DiscardOldestPolicy(忽略⽼任务)1.5 ⾃定义拒绝策略⼀、拒绝策略(JDK提供了4种,另外也可以⾃定义拒绝策略,因此总共有5种。
)线程池中的线程已经⽤完了,⽆法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。
这时候我们就需要拒绝策略机制合理的处理这个问题。
JDK 内置的拒绝策略如下:1.AbortPolicy :直接抛出异常,阻⽌系统正常运⾏。
2.CallerRunsPolicy :只要线程池未关闭,该策略直接在调⽤者线程中,运⾏当前被丢弃的任务。
显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
3.DiscardPolicy :该策略默默地丢弃⽆法处理的任务,不予任何处理。
如果允许任务丢失,这是最好的⼀种⽅案。
4.DiscardOldestPolicy :丢弃最⽼的⼀个请求,也就是即将被执⾏的⼀个任务,并尝试再次提交当前任务。
以上内置拒绝策略均实现了 RejectedExecutionHandler 接⼝,若以上策略仍⽆法满⾜实际需要,完全可以⾃⼰扩展RejectedExecutionHandler 接⼝。
1.1 AbortPolicy(默认拒绝策略)(也可以没有new ThreadPoolExecutor.AbortPolicy() 这个参数,隐式的默认拒绝策略)import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadDemo58 {public static void main(String[] args) {// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 11; i++) {int finalI = i;executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("任务:" + finalI + ",线程名:" +Thread.currentThread().getName());}});}}}1.2 CallerRunsPolicy(使⽤调⽤线程池的线程来执⾏任务)(即使⽤主线程来执⾏任务)import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadDemo59 {public static void main(String[] args) throws InterruptedException { // 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.CallerRunsPolicy());for (int i = 0; i < 11; i++) {int finalI = i;executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("任务:" + finalI + ",线程名:" + Thread.currentThread().getName());}});// Thread.sleep(200);}}}1.3 DiscardPolicy (忽略新任务)import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadDemo59 {public static void main(String[] args) throws InterruptedException { // 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.AbortPolicy());for (int i = 0; i < 11; i++) {int finalI = i;executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("任务:" + finalI + ",线程名:" + Thread.currentThread().getName());}});}}}1.4 DiscardOldestPolicy(忽略⽼任务)(⽼任务指第⼀个进⼊阻塞队列⾥的)import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadDemo59 {public static void main(String[] args) throws InterruptedException { // 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 0; i < 11; i++) {int finalI = i;executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("任务:" + finalI + ",线程名:" +Thread.currentThread().getName());}});}}}1.5 ⾃定义拒绝策略import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.RejectedExecutionHandler;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadDemo60 {public static void main(String[] args) throws InterruptedException {// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 5,0, TimeUnit.SECONDS,new LinkedBlockingQueue<>(5),new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {// ⾃定义拒绝策略System.out.println("执⾏了⾃定义拒绝策略");}});for (int i = 0; i < 11; i++) {int finalI = i;executor.execute(new Runnable() {@Overridepublic void run() {System.out.println("任务:" + finalI + ",线程名:" +Thread.currentThread().getName());}});}}}到此这篇关于详解什么是线程池的拒绝策略?的⽂章就介绍到这了,更多相关线程池的拒绝策略内容请搜索以前的⽂章或继续浏览下⾯的相关⽂章希望⼤家以后多多⽀持!。
线程池基本工作流程
线程池是一种用于管理线程的机制,它可以在应用程序启动时预先创建一组线程,并将这些线程放入一个线程池中。
当应用程序需要执行任务时,可以从线程池中获取一个线程来执行任务,执行完成后将线程返回到线程池中以供下次使用。
线程池的工作流程如下:
1、创建线程池
线程池的创建通常在应用程序启动时完成。
创建线程池时,需要指定线程池的核心线程数和最大线程数。
核心线程数是指在应用程序启动时创建的线程数,而最大线程数是指线程池中允许的最大线程数。
2、提交任务
当应用程序需要执行任务时,可以将任务提交给线程池。
提交任务时,需要指定任务的类型和参数。
3、任务调度
当任务提交给线程池后,线程池会根据任务的优先级和队列情况来调度任务。
如果当前队列已满,则线程池会根据其配置来创建新的线程或使用现有线程来执行任务。
4、任务执行
当线程池获取到任务后,它会从线程池中获取一个可用的线程来执行任务。
在执行任务期间,线程池会监控任务的执行情况,如是否超时、是否抛出异常等。
5、任务完成
当任务执行完成后,线程池会回收该线程并将其返回到线程池中以供下次使用。
如果该线程因执行任务而被阻塞或异常结束,则线程池会根据其配置来创建新的线程或使用现有线程来代替它。
总之,线程池的工作流程包括创建线程池、提交任务、任务调度、任务执行和任务完成等步骤。
通过使用线程池,可以有效地管理应用程序中的线程,提高应用程序的性能和可靠性。
Java线程池使⽤和常⽤参数多线程问题:1、java中为什么要使⽤多线程使⽤多线程,可以把⼀些⼤任务分解成多个⼩任务来执⾏,多个⼩任务之间互不影像,同时进⾏,这样,充分利⽤了cpu资源。
2、java中简单的实现多线程的⽅式继承Thread类,重写run⽅法;12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28class MyTread extends Thread{public void run() { System.out.println(Thread.currentThread().getName());}}实现Runable接⼝,实现run⽅法;class MyRunnable implements Runnable{ public void run() { System.out.println(Thread.currentThread().getName()); }}class ThreadTest { public static void main(String[] args) { MyTread thread = new Mythread(); thread.start(); //开启⼀个线程 MyRunnable myRunnable = new MyRunnable(); Thread runnable = new Thread(myRunnable); runnable.start(); //开启⼀个线程 }}3、java线程的状态创建:当new了⼀个线程,并没有调⽤start之前,线程处于创建状态;就绪:当调⽤了start之后,线程处于就绪状态,这是,线程调度程序还没有设置执⾏当前线程;运⾏:线程调度程序执⾏到线程时,当前线程从就绪状态转成运⾏状态,开始执⾏run⽅法⾥边的代码;阻塞:线程在运⾏的时候,被暂停执⾏(通常等待某项资源就绪后在执⾏,sleep、wait可以导致线程阻塞),这是该线程处于阻塞状态;死亡:当⼀个线程执⾏完run⽅法⾥边的代码或调⽤了stop⽅法后,该线程结束运⾏4、为什么要引⼊线程池当我们需要的并发执⾏线程数量很多时,且每个线程执⾏很短的时间就结束了,这样,我们频繁的创建、销毁线程就⼤⼤降低了⼯作效率(创建和销毁线程需要时间、资源)。
线程池的参数配置线程池是一种用来管理多个线程的技术,可以显著提高多线程程序的性能和效率。
合理的线程池参数配置对于实现高并发和提高系统的稳定性至关重要。
本文将介绍线程池的常见参数配置,并提供一些最佳实践的建议。
1. 核心线程数(corePoolSize):核心线程数是线程池初始化时创建的线程数量。
这些线程处于一直活动的状态,并且在线程空闲时也不会被销毁。
核心线程数的设置需要根据系统的负载情况和处理能力进行调整。
一般来说,可以按照CPU核心数来设置核心线程数,在4-8之间选择一个适当的值。
2. 最大线程数(maximumPoolSize):最大线程数是线程池的最大容量,表示线程池中最多能创建多少个线程。
在核心线程都处于运行状态且工作队列已满时,线程池会创建新的线程,直到达到最大线程数。
最大线程数需要根据系统的负载情况和内存资源来进行设置。
3. 空闲线程存活时间(keepAliveTime):空闲线程存活时间指的是当线程池中的线程数量大于核心线程数且没有可处理的任务时,多余的空闲线程在被销毁前的等待时间。
如果线程在指定的时间内没有任务可执行,那么它将被销毁。
可以根据任务的平均执行时间和系统响应时间来进行设置,一般建议设置为1-5分钟。
4. 工作队列(workQueue):工作队列用来存放待执行的任务。
线程池中的线程会从工作队列中获取任务进行执行。
Java提供了多种类型的工作队列,如ArrayBlockingQueue、LinkedBlockingQueue等,选择适合系统需求的队列类型,可以根据任务量和性能需求来选择。
5. 拒绝策略(RejectedExecutionHandler):当线程池中的线程数量达到最大线程数且工作队列已满时,新提交的任务将会被拒绝执行。
拒绝策略用来定义当线程池饱和时如何处理新的任务。
Java提供了几种默认的拒绝策略,如AbortPolicy、CallerRunsPolicy等。
浅谈Java线程池的7⼤核⼼参数⽬录前⾔⼀、线程池的创建及重要参数⼆、ThreadPoolExecutor中重要的⼏个参数详解三、workQueue队列(阻塞队列)四、常见的⼏种⾃动创建线程池⽅式五、线程池实现线程复⽤的原理六、⼿动创建线程池(推荐)七、Springboot中使⽤线程池前⾔java中经常需要⽤到多线程来处理⼀些业务,我不建议单纯使⽤继承Thread或者实现Runnable接⼝的⽅式来创建线程,那样势必有创建及销毁线程耗费资源、线程上下⽂切换问题。
同时创建过多的线程也可能引发资源耗尽的风险,这个时候引⼊线程池⽐较合理,⽅便线程任务的管理。
java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的⼏个核⼼类及接⼝包括:Executor、Executors、ExecutorService、ThreadPoolExecutor、FutureTask、Callable、Runnable等。
⼀、线程池的创建及重要参数线程池可以⾃动创建也可以⼿动创建,⾃动创建体现在Executors⼯具类中,常见的可以创建newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool;⼿动创建体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:public static ExecutorService newFixedThreadPool(int var0) {return new ThreadPoolExecutor(var0, var0, 0L, LISECONDS, new LinkedBlockingQueue());}public static ExecutorService newSingleThreadExecutor() {return new Executors.FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, LISECONDS, new LinkedBlockingQueue()));}public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());}public static ScheduledExecutorService newScheduledThreadPool(int var0) {return new ScheduledThreadPoolExecutor(var0);}(重点)public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {……}⼆、ThreadPoolExecutor中重要的⼏个参数详解corePoolSize:核⼼线程数,也是线程池中常驻的线程数,线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执⾏任务maximumPoolSize:最⼤线程数,在核⼼线程数的基础上可能会额外增加⼀些⾮核⼼线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize 的线程(线程池总线程数不超过maxPoolSize)keepAliveTime:⾮核⼼线程的空闲时间超过keepAliveTime就会被⾃动终⽌回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作⽤了(因为不存在⾮核⼼线程);unit:keepAliveTime的时间单位workQueue:⽤于保存任务的队列,可以为⽆界、有界、同步移交三种队列类型之⼀,当池⼦⾥的⼯作线程数⼤于corePoolSize时,这时新进来的任务会被放到队列中threadFactory:创建线程的⼯⼚类,默认使⽤Executors.defaultThreadFactory(),也可以使⽤guava库的ThreadFactoryBuilder来创建handler:线程池⽆法继续接收任务(队列已满且线程数达到maximunPoolSize)时的饱和策略,取值有AbortPolicy、CallerRunsPolicy、DiscardOldestPolicy、DiscardPolicy线程池中的线程创建流程图:(基于<Java并发编程的艺术>⼀书)举个例⼦:现有⼀个线程池,corePoolSize=10,maxPoolSize=20,队列长度为100,那么当任务过来会先创建10个核⼼线程数,接下来进来的任务会进⼊到队列中直到队列满了,会创建额外的线程来执⾏任务(最多20个线程),这个时候如果再来任务就会执⾏拒绝策略。
线程池参数详解JDK1.5中引⼊了强⼤的concurrent包,其中最常⽤的莫过了线程池的实现ThreadPoolExecutor,它给我们带来了极⼤的⽅便,但同时,对于该线程池不恰当的设置也可能使其效率并不能达到预期的效果,甚⾄仅相当于或低于单线程的效率。
ThreadPoolExecutor类可设置的参数主要有:corePoolSize在创建了线程池后,默认情况下,线程池中并没有任何线程,⽽是等待有任务到来才创建线程去执⾏任务,(除⾮调⽤了prestartAllCoreThreads()或者prestartCoreThread()⽅法,从这2个⽅法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者⼀个线程)。
默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建⼀个线程去执⾏任务,当线程池中的线程数⽬达到corePoolSize后,就会把到达的任务放到缓存队列当中。
核⼼线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
maxPoolSize当线程数⼤于或等于核⼼线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。
如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能⼒,线程池会拒绝处理任务⽽抛出异常。
keepAliveTime当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。
如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
allowCoreThreadTimeout是否允许核⼼线程空闲退出,默认值为false。
queueCapacity任务队列容量。
从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。
JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介
在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多对并发特性的支持,例如:线程池。
一、简介
线程池类为 Java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue< Runnable> workQueue,
RejectedExecutionHandler handler)
corePoolSize:线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime:线程池维护线程所允许的空闲时间
unit:线程池维护线程所允许的空闲时间的单位
workQueue:线程池所使用的缓冲队列
handler:线程池对拒绝任务的处理策略
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。
这样,线程池可以动态的调整池中的线程数。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue我常用的是:java.util.concurrent.ArrayBlockingQueue
handler有四个选择:
ThreadPoolExecutor.AbortPolicy()
抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()
重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy()
抛弃当前的任务
二、一般用法举例
//------------------------------------------------------------
//TestThreadPool.java
//package cn.simplelife.exercise;
import java.io.Serializable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThreadPool {
private static int produceTaskSleepTime = 2;
private static int consumeTaskSleepTime = 2000;
private static int produceTaskMaxNumber = 10;
public static void main(String[] args) {
//构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,
TimeUnit.SECONDS, new ArrayBlockingQueue< Runnable>(3),
new ThreadPoolExecutor.DiscardOldestPolicy());
for(int i=1;i< =produceTaskMaxNumber;i++){
try {
//产生一个任务,并将其加入到线程池
String task = "task@ " + i;
System.out.println("put " + task);
threadPool.execute(new ThreadPoolTask(task));
//便于观察,等待一段时间
Thread.sleep(produceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 线程池执行的任务
* @author hdpan
*/
public static class ThreadPoolTask implements Runnable,Serializable{ private static final long serialVersionUID = 0;
//保存任务所需要的数据
private Object threadPoolTaskData;
ThreadPoolTask(Object tasks){
this.threadPoolTaskData = tasks;
}
public void run(){
//处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
System.out.println("start .."+threadPoolTaskData);
try {
////便于观察,等待一段时间
Thread.sleep(consumeTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
threadPoolTaskData = null;
}
public Object getTask(){
return this.threadPoolTaskData;
}
}
}
//------------------------------------------------------------
说明:
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask 类型的对象。
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个*的领导,他派发出许多任务,丢给一个叫threadPool的任劳任怨的小组来做。
这个小组里面队员至少有两个,如果他们两个忙不过来,任务就被放到任务列表里面。
如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。
但是基于成本的考虑,不能雇佣太多的队员,至多只能雇佣 4个。
如果四个队员都在忙时,再有新的任务,这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发,直到接受这个任务为止(更*!呵呵)。
因为队员工作是需要成本的,如果工作很闲,闲到 3SECONDS都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。
4、通过调整 produceTaskSleepTime和 consumeTaskSleepTime的大小来实现对派发任务和处理任务的速度的控制,改变这两个值就可以观察不同速率下程序的工作情况。
5、通过调整4中所指的数据,再加上调整任务丢弃策略,换上其他三种策略,就可以看出不同策略下的不同处理方式。
6、对于其他的使用方法,参看jdk的帮助,很容易理解和使用。