java executor Java线程池ThreadPoolExecutor使用和分析(一)

java executor Java线程池ThreadPoolExecutor使用和分析(一)

一、线程池架构

概括一下:

是最基础的执行接口;

接口继承了,在其上做了一些()、()的扩展java executor,可以说是真正的线程池接口;

ice抽象类实现了接口中的大部分方法;

继承了ice,是线程池的具体实现;

vice接口继承了接口,提供了带”周期执行”功能;

既继承了线程池,也实现了vice接口,是带”周期执行”功能的线程池;

是线程池的静态工厂,其提供了快捷创建线程池的静态方法。

1、接口

“执行者”接口,只提供了一个方法:

 void execute(Runnable command);

可以用来执行已经提交的任务对象,这个接口提供了一种将“任务提交”与“任务执行”解耦的方法。

2、接口

“执行者服务”接口,可以说是真正的线程池接口,在接口的基础上做了一些扩展,主要是

(A) 管理任务如何终止的 相关方法

/**
 * 启动一次有序的关闭,之前提交的任务执行,但不接受新任务
 * 这个方法不会等待之前提交的任务执行完毕
 */
void shutdown();
/**
 * 试图停止所有正在执行的任务,暂停处理正在等待的任务,返回一个等待执行的任务列表
 * 这个方法不会等待正在执行的任务终止
 */
List shutdownNow();
/**
 * 如果已经被shutdown,返回true
 */
boolean isShutdown();
/**
 * 如果所有任务都已经被终止,返回true
 * 是否为终止状态
 */
boolean isTerminated();
/**
 * 在一个shutdown请求后,阻塞的等待所有任务执行完毕
 * 或者到达超时时间,或者当前线程被中断
 */
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

(B) 可以生成用于追踪一个或多个异步任务执行结果的对象的 ()相关方法

/**
 * 提交一个可执行的任务,返回一个Future代表这个任务
 * 等到任务成功执行,Future#get()方法会返回null
 */
Future submit(Runnable task);
/**
 * 提交一个可以执行的任务,返回一个Future代表这个任务
 * 等到任务执行结束,Future#get()方法会返回这个给定的result
 */
 Future submit(Runnable task, T result);
/**
 * 提交一个有返回值的任务,并返回一个Future代表等待的任务执行的结果
 * 等到任务成功执行,Future#get()方法会返回任务执行的结果
 */
 Future submit(Callable task);

3、vice接口

/**
 * 在给定延时后,创建并执行一个一次性的Runnable任务
 * 任务执行完毕后,ScheduledFuture#get()方法会返回null
 */
public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit);
/**
 * 在给定延时后,创建并执行一个ScheduledFutureTask
 * ScheduledFuture 可以获取结果或取消任务
 */
public  ScheduledFuture schedule(Callable callable, ong delay, TimeUnit unit);
/**
 * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期
 * 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
 * 如果执行任务发生异常,随后的任务将被禁止,否则任务只会在被取消或者Executor被终止后停止
 * 如果任何执行的任务超过了周期,随后的执行会延时,不会并发执行
 */
public ScheduledFuture scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
/**
 * 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟
 * 如果执行任务发生异常,随后的任务将被禁止,否则任务只会在被取消或者Executor被终止后停止
 */
public ScheduledFuture scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

二、

1、构造参数

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于;

如果当前线程数为,继续提交的任务被保存到阻塞队列中,等待被执行;

如果执行了线程池的ds()方法,线程池会提前创建并启动所有核心线程。

线程池中允许的最大线程数。如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于

线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间。默认情况下,该参数只在线程数大于时才有用

必须是阻塞队列。当线程池中的线程数超过它的的时候,线程会进入阻塞队列进行阻塞等待。通过,线程池实现了阻塞功能

几种排队的策略:

(1)不排队,直接提交

将任务直接交给线程处理而不保持它们,可使用

如果不存在可用于立即运行任务的线程(即线程池中的线程都在工作),则试图把任务加入缓冲队列将会失败,因此会构造一个新的线程来处理新添加的任务,并将其加入到线程池中(–>扩容)

.()采用的便是这种策略

(2)无界队列

可以使用(基于链表的有界队列,FIFO),理论上是该队列可以对无限多的任务排队

将导致在所有线程都工作的情况下将新任务加入到队列中。这样,创建的线程就不会超过,也因此,的值也就无效了

(3)有界队列

可以使用(基于数组结构的有界队列,FIFO),并指定队列的最大长度

使用有界队列可以防止资源耗尽,但也会造成超过队列大小和后,提交的任务被拒绝的问题,比较难调整和控制。

创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名

/**
 * The default thread factory
 */
static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
    DefaultThreadFactory() {
        SecurityManager s = System.getSecurityManager();
        group = (s != null) ? s.getThreadGroup() :
                              Thread.currentThread().getThreadGroup();
        namePrefix = "pool-" +
                      poolNumber.getAndIncrement() +
                     "-thread-";
    }
    public Thread newThread(Runnable r) {
        Thread t = new Thread(group, r,
                              namePrefix + threadNumber.getAndIncrement(),
                              0);
        if (t.isDaemon())
            t.setDaemon(false);
        if (t.getPriority() != Thread.NORM_PRIORITY)
            t.setPriority(Thread.NORM_PRIORITY);
        return t;
    }
}

静态工厂里默认的,线程的命名规则是“pool-数字–数字”

dler(饱和策略)

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

(1):直接抛出异常,默认策略;

(2):用调用者所在的线程来执行任务;

(3):丢弃阻塞队列中靠最前的任务,并执行当前任务;

(4):直接丢弃任务;

当然也可以根据应用场景实现dler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

2、线程池执行流程

根据源码前面大段的注释,我们可以看出,当试图通过方法将一个任务添加到线程池中时,按照如下顺序来处理:

(1)如果线程池中的线程数量少于,就创建新的线程来执行新添加的任务;

(2)如果线程池中的线程数量大于等于java executor,但队列未满,则将新添加的任务放到中,按照FIFO的原则依次等待执行(线程池中有线程空闲出来后依次将队列中的任务交付给空闲的线程执行);

(3)如果线程池中的线程数量大于等于,且队列已满,但线程池中的线程数量小于,则会创建新的线程来处理被添加的任务;

(4)如果线程池中的线程数量等于了,就用dler来做拒绝处理

总结,当有新的任务要处理时,先看线程池中的线程数量是否大于,再看缓冲队列是否满,最后看线程池中的线程数量是否大于

另外java executor,当线程池中的线程数量大于时,如果里面有线程的空闲时间超过了,就将其移除线程池

最后,通过下面的图来看看线程池中的任务调度策略:

图1

图2

说明:

在图1中,线程池中有N个任务。”任务1″, “任务2”, “任务3″这3个任务在执行,而”任务3″到”任务N”在阻塞队列中等待。正在执行的任务,在集合中,集合包含3个,每一个对应一个线程,线程每次处理一个任务。

当集合中处理完某一个任务之后,会从阻塞队列中取出一个任务来继续执行,如图2所示。图2表示”任务1″处理完毕之后,线程池将”任务4″从阻塞队列中取出,放到中进行处理。

三、静态工厂创建几种常用线程池

工厂类提供了线程池的初始化接口,主要有如下几种:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue(),
                                  threadFactory);
}

创建一个指定工作线程数的线程池,其中参数 和 相等,阻塞队列基于

它是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是在线程池空闲时,即线程池中没有可运行任务时,它也不会释放工作线程,还会占用一定的系统资源

tor

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue(),
                                threadFactory));
}

初始化的线程池中只有一个线程,如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用作为阻塞队列

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue(),
                                  threadFactory);
}

创建一个可缓存工作线程的线程池,默认存活时间60秒,线程池的线程数可达到.,即,内部使用作为阻塞队列;

在没有任务执行时,当线程的空闲时间超过,则工作线程将会终止,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销

ol

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public static ScheduledExecutorService newScheduledThreadPool(
        int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据

注意:

vice#() 指的是“以固定的频率”执行,(周期)指的是两次成功执行之间的时间

比如,(, 5, 2, ),第一次开始执行是5s后,假如执行耗时1s,那么下次开始执行是7s后java executor Java线程池ThreadPoolExecutor使用和分析(一),再下次开始执行是9s后

而vice#ay()  指的是“以固定的延时”执行,delay(延时)指的是一次执行终止和下一次执行开始之间的延迟

还是上例,ay(, 5, 2, ),第一次开始执行是5s后,假如执行耗时1s,执行完成时间是6s后,那么下次开始执行是8s后java executor Java线程池ThreadPoolExecutor使用和分析(一),再下次开始执行是11s后

参考资料:

38. Java多线程系列–“JUC线程池”01之 线程池架构

深入分析java线程池的实现原理

聊聊并发(三)Java线程池的分析和使用

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » java executor Java线程池ThreadPoolExecutor使用和分析(一)

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们