JavaDemo——java线程池

本文详细介绍了Java线程池的五种状态及其转换,包括RUNNING、SHUTDOWN、STOP、TIDYING和TERMINATED。讲解了ThreadPoolExecutor的构造方法、参数含义,特别是核心线程数、最大线程数、阻塞队列类型和拒绝策略。还讨论了线程池的工作方式、线程超时设置以及Executors提供的线程池创建方法。最后提到了阿里Java开发手册关于线程池使用的建议。

之前曾给公司的小伙伴们讲过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
        1.直接提交队列:SynchronousQueue<E>,这是一个没有容量的阻塞队列,插入数据的时候阻塞等待被取走,取数据的时候阻塞等待有数据插入;
        2.有界队列:ArrayBlockingQueue<E>,底层数组实现,必须设定大小,存取使用了一个锁,存取不创建销毁元素;
        3.无界队列:LinkedBlockingQueue<E>,底层链表实现,可不设定大小(不设定使用int最大值),存取两个锁,并发性更好,存取会创建销毁元素;
        4.优先级队列:PriorityBlockingQueue<E>,根据优先级排序取出;
threadFactory:线程工厂( not null ),有默认的线程工厂Executors.defaultThreadFactory(),也可以实现ThreadFactory接口重写newThread方法自定义创建线程过程;
Handler:拒绝策略( not null ),ThreadPoolExecutor提供了4种策略(内部类),我们也可以自己实现RejectedExecutionHandler接口自己处理;
1.AbortPolicy:直接抛出一个RejectedExecutionException异常(默认策略);
2.DiscardPolicy:丢弃该任务;
3.DiscardOldestPolicy:丢弃队列中最老的一个任务并重新提交该任务;
4.CallerRunsPolicy:用调用者线程执行该任务;
 

 ThreadPoolExecutor的运作方式:

1.当线程数小于corePoolSize时,直接创建线程执行,不管线程池里的空闲线程;
2.当线程数达到corePoolSize时,会向workQueue队列里添加该任务;
3.当队列无法添加时,并且线程数小于maximumPoolSize,则会继续创建新的线程执行任务;
4.当队列无法添加并且线程数达到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类还有3protected方法(都是空的方法,需要自己继承该类实现或者用匿名类实现):

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也会启动一个线程去执行任务);

TimerScheduledExecutorService区别:

Timer是基于绝对时间的,对系统的时间改变是敏感的,而executor是相对时间,修改系统时间不会造成影响;
Timer不会捕获异常,如果timertask抛出未捕获异常将导致整个Timer取消,剩下的未执行的任务都无法执行,新任务也无法执行;
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可以使用ForkJoinPoolinvoke方法,普通任务也可以用普通线程池的方法(继承了AbstractExecutorService类);

线程池的线程是守护线程,如果不是cpu密集型任务耗时会比普通的线程池长,ForkJoinPool可以用有限数量的线程完成非常多的父子关系的任务,这比普通线程池优异;

 

 运行结果:

 

 

ThreadPoolExecutor线程池的阻塞队列大小决定了可以容纳等待执行的任务数量。在ThreadPoolExecutor中,可以使用不同的阻塞队列实现来控制任务的排队和执行。 常用的阻塞队列实现有以下几种: 1. 直接提交队列(SynchronousQueue):该队列不存储任务,而是直接将任务提交给线程池中的工作线程进行执行,如果没有可用的工作线程,则尝试创建一个新线程来执行任务。这种情况下,设置队列大小为0或者1没有意义。 2. 有界队列(ArrayBlockingQueue):该队列有固定的容量,可以在创建ThreadPoolExecutor时指定队列的大小。当线程池中的线程数达到最大线程数时,后续的任务会被放入到该队列中等待执行。 3. 无界队列(LinkedBlockingQueue):该队列没有固定的容量,可以根据需要动态地增加其大小。当线程池中的线程数达到最大线程数时,后续的任务会被放入到该队列中等待执行。因为队列没有大小限制,所以可能会导致内存溢出。 4. 优先级队列(PriorityBlockingQueue):该队列根据任务的优先级进行排序,具有更高优先级的任务会被优先执行。可以在创建ThreadPoolExecutor时指定比较器来定义任务的优先级。 需要根据具体的场景和需求选择合适的阻塞队列实现和大小设置。如果任务量较大,可以选择有界队列或者优先级队列,以控制线程池的负载。如果任务量不确定或者需要动态增加队列的大小,可以选择无界队列。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值