之前曾给公司的小伙伴们讲过java线程池,遂把ppt拷过来;
线程池状态
RUNNING
线程池创建后就是running状态;
可以处理已添加的任务,可以新添加任务;
SHUTDOWN
调用线程池的shutdown()后;
可以处理已添加的任务(正在执行和队列里),不可以新加任务了;
STOP
running或者shutdown状态调用线程池的shutdownNow()后;
给正在执行的任务发中断指令,不处理队列里的任务,不可添加新任务;
TIDYING
shutdown状态队列为空并且线程池任务全完成,或者stop状态线程池任务全完成;
自动执行ThreadPoolExecutor类的terminated()方法,该方法是protected方法,可以重写自定义该方法;
TERMINATED
terminated()执行完毕后;
java里的线程池ThreadPoolExecutor类
构造方法有4个:
继承关系:
参数最多的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
各个参数的解释如下:
corePoolSize :核心线程数( >=0 )
maximumPoolSize :最大线程数( >0 , >= corePoolSize )
keepAliveTime :空闲线程存活时间( >=0 ),默认只对超过核心线程数时有效,可以通过 allowCoreThreadTimeOut ( boolean ) 方法设置;
Unit :时间单位, java.util.concurrent.TimeUnit 枚举的单位,包括 NANOSECONDS (纳秒)、 MICROSECONDS (微秒)、 MILLISECONDS (毫秒)、 SECONDS (秒)、 MINUTES (分钟)、 HOURS (小时)、 DAYS (天);
workQueue :阻塞队列( not null )
直接提交队列: SynchronousQueue <E> ,这是一个没有容量的阻塞队列,插入数据的时候阻塞等待被取走,取数据的时候阻塞等待有数据插入;
有界队列: ArrayBlockingQueue <E> ,底层数组实现,必须设定大小,存取使用了一个锁,存取不创建销毁元素;
无界队列: LinkedBlockingQueue <E> ,底层链表实现,可不设定大小(不设定使用 int 最大值),存取两个锁,并发性更好,存取会创建销毁元素;
优先级队列: PriorityBlockingQueue <E> ,根据优先级排序取出;
threadFactory :线程工厂( not null ),有默认的线程工厂 Executors.defaultThreadFactory () ,也可以实现 ThreadFactory 接口重写 newThread 方法自定义创建线程过程;
Handler :拒绝策略( not null ), ThreadPoolExecutor 提供了 4 种策略(内部类),我们也可以自己实现 RejectedExecutionHandler 接口自己处理;
AbortPolicy :直接抛出一个 RejectedExecutionException 异常(默认策略);
DiscardPolicy :丢弃该任务;
DiscardOldestPolicy :丢弃队列中最老的一个任务并重新提交该任务;
CallerRunsPolicy :用调用者线程执行该任务;
ThreadPoolExecutor的运作方式:
当线程数小于 corePoolSize 时,直接创建线程执行,不管线程池里的空闲线程;
当线程数达到 corePoolSize 时,会向 workQueue 队列里添加该任务;
当队列无法添加时,并且线程数小于 maximumPoolSize ,则会继续创建新的线程执行任务;
当队列无法添加并且线程数达到 maximumPoolSize ,则会执行拒绝策略;
注:线程池中的线程都是相同的,并没有区分核心线程和非核心线程,只是用数字表示线程数量,当线程池里线程数量超过核心线程数量时,空闲线程会根据超时时间自己销毁,直到线程数降到核心线程数为止(默认情况),如果设置了allowCoreThreadTimeOut(true),则所有线程都会按照空闲超时时间销毁;
ThreadPoolExecutor的其他方法:
public void allowCoreThreadTimeOut ( boolean
value)
:设置允许核心线程超时销毁;
public boolean allowsCoreThreadTimeOut ()
public boolean awaitTermination (long timeout,
TimeUnit
unit)
:等待线程池
termination
,超时返回
false
,超时前
termination
返回
true
;
public void execute(Runnable command)
public int getActiveCount ()
:返回正在执行任务的线程数的大概值;
public long getCompletedTaskCount () :返回已经完成的任务数量的大概值;
public int getCorePoolSize ()
public long getKeepAliveTime ( TimeUnit
unit)
public int getLargestPoolSize ()
:返回线程池同一时间持有的最多线程数;
public int getMaximumPoolSize ()
public int getPoolSize ()
:返回现在线程池中线程的数量;
public BlockingQueue <Runnable> getQueue
()
public RejectedExecutionHandler getRejectedExecutionHandler ()
public long getTaskCount () :返回已经被安排任务总数大概值;
public ThreadFactory getThreadFactory ()
public boolean isShutdown ()
public boolean isTerminated ()
public boolean isTerminating ()
public int prestartAllCoreThreads ()
:启动所有核心线程,让它们空闲的等待工作,返回启动的数量;
public boolean prestartCoreThread ()
:启动一个核心线程空闲等待工作,如果所有核心线程都启动了则返回
false
;
public void purge() :尝试从队列中删除已经取消的任务,回收存储空间,任务取消后可能留在队列中等工作线程删除,不过有其他线程干扰时可能失败;
public boolean remove(Runnable task)
public void setCorePoolSize ( int
corePoolSize
)
public void setKeepAliveTime (long time, TimeUnit
unit)
public void setMaximumPoolSize ( int
maximumPoolSize
)
public void setRejectedExecutionHandler ( RejectedExecutionHandler
handler)
public void setThreadFactory ( ThreadFactory
threadFactory
)
public void shutdown()
public List<Runnable> shutdownNow () :尝试停止正在执行的任务,停止处理等待处理的任务,返回队列里未执行的任务列表,但无法响应中断操作的任务不会终止;
public String toString ()
ThreadPoolExecutor类还有3个protected方法(都是空的方法,需要自己继承该类实现或者用匿名类实现):
protected void beforeExecute (Thread t, Runnable r)
protected void afterExecute (Runnable r, Throwable
t)
protected void terminated()
还有继承父类AbstractExecutorService类的submit方法:
public Future<?> submit(Runnable task)
public <T> Future<T> submit(Runnable task, T result)
public <T> Future<T> submit(Callable<T> task)
Executors类提供的简单创建线程池的方法
Executors.newCachedThreadPool();
Executors.newFixedThreadPool(int nThreads);
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(int corePoolSize);
Executors.newSingleThreadScheduledExecutor ();
Executors.newWorkStealingPool ();
以上方法除了最后一个,前5个方法各有一个重载方法,多了一个threadFactory参数用来创建线程的工厂,newWorkStealingPool方法的重载方法参数是一个int值,用来表示并行性(跟cpu核心数相关);
阿里的java开发手册指出必须用线程池创建线程,并且不可以使用Executors提供的这些方法,要根据业务合理创建线程池,避免资源耗尽;
方法详解:
•newCachedThreadPool()
核心线程数为0,没有核心线程,队列是SynchronousQueue这个特殊的无容量阻塞队列,当没有空闲线程时所有任务都是直接创建非核心线程执行(会重用空闲线程),最多可以创建Integer最大值的线程数(资源消耗上限过大),超时时间60s,创建的非核心线程空闲超时60s都会自动销毁;
•newFixedThreadPool(int nThreads)
核心线程数和最大线程数都是参数nThreads,超时时间0ms,队列是无界阻塞队列(int最大值),所以最开始每来一个任务都是创建新线程执行任务,任务执行完毕新线程就空闲等待了,当线程数达到nThreads个时,再来任务就会向队列里添加任务,空闲等待的核心线程取走队列里的任务,由于队列足够大,最大线程数是失效状态,超时时间也是失效的状态;
•newSingleThreadExecutor()
使用内部类对ExecutorService的方法做了限制保证单线程,当线程故障时会重新申请一个线程代替故障线程;
•newScheduledThreadPool(int corePoolSize)
返回值ScheduledExecutorService是一个接口,定义了4个跟定时执行任务有关的方法;
返回的时候实例化了一个ScheduledThreadPoolExecutor类;
构造方法里调用了ThreadPoolExecutor的方法;
DelayedWorkQueue是个基于数组的最小堆优先级阻塞队列(最顶点的值最小),每次取任务的时候总是把时间最靠前的取出来;
可调用方法有4个:
public ScheduledFuture <?> schedule(Runnable command, long delay, TimeUnit
unit)
public <V> ScheduledFuture <V> schedule(Callable<V> callable, long delay, TimeUnit
unit)
以上两个方法是延时执行任务;
public ScheduledFuture <?> scheduleAtFixedRate
(Runnable command,
long initialDelay,
long period,
TimeUnit unit)
在initialDelay时间后开始以固定时间频率period周期性执行任务;
下一次的开始时间=上一次的开始时间+period;
如果某次任务执行时间超过下次任务的开始时间,则该长时间任务结束后立即会执行下次任务(立即执行推迟的任务已保持固定频率);
public ScheduledFuture <?> scheduleWithFixedDelay
(Runnable command,
long initialDelay,
long delay,
TimeUnit unit)
在initialDelay时间后开始以固定时间延迟delay周期性执行任务;
下一次的开始时间=上一次的结束时间+delay;
注:这4个方法跟ThreadPoolExecutor执行过程略有不同,会先将任务放入队列,然后执行一个类似prestartCoreThread()的方法启动一个线程去队列获取任务(即使创建线程池时参数传入0也会启动一个线程去执行任务);
Timer和ScheduledExecutorService区别:
Timer 是基于绝对时间的,对系统的时间改变是敏感的,而 executor 是相对时间,修改系统时间不会造成影响;
Timer 不会捕获异常,如果 timertask 抛出未捕获异常将导致整个 T imer 取消,剩下的未执行的任务都无法执行,新任务也无法执行;
Timer 是单线程的,如果某个任务时间执行过长,会影响后面的其他任务的执行;
•newSingleThreadScheduledExecutor()
跟newSingleThreadExecutor()原理基本一样;
•newWorkStealingPool()和newWorkStealingPool(int parallelism)
工作窃取线程池使用的是ForkJoinPool线程池(分治思想),线程用的ForkJoinWorkerThread类(继承了Thread),任务用的是ForkJoinTask类(两个常用子类:RecursiveAction和带返回值RecursiveTask<V>,继承并实现compute()方法编写自己的分治任务,还有个CountedCompleter<T>),执行的线程数跟cpu的核心数相关( 通常一个核心上跑一个线程,cpu并行执行任务,parallelism参数不能超过32767),该线程池适合cpu密集型任务,充分利用多cpu多核心的性能;
线程池的任务不是提交给线程池的队列,每个线程有个线程本地队列wokrQueue(双端队列),线程池会将任务平均分到每个线程的本地队列(尾),线程使用LIFO从本地队列取任务(尾),本地任务队列无任务则从其他线程队列按照FIFO取任务(头,优先查看曾经从自己队列窃取任务的线程队列),线程会顺序从队列取任务,不会因为当前任务阻塞而切换任务(不适用阻塞任务如I/O、线程同步、sleep等);
ForkJoinTask可以使用ForkJoinPool的invoke方法,普通任务也可以用普通线程池的方法(继承了AbstractExecutorService类);
线程池的线程是守护线程,如果不是cpu密集型任务耗时会比普通的线程池长,ForkJoinPool可以用有限数量的线程完成非常多的父子关系的任务,这比普通线程池优异;
运行结果: