5、线程池

什么是线程池?为什么要使用线程池?
1、降低资源的消耗,降低线程创建和销毁的资源消耗

2、提高响应速度,避免创建销毁消耗时间;

3、提高线程的可管理性

实现一个我们自己的线程池
1、线程必须在池子已经创建好,并且可以保持住,要有容器保存多个线程

2、线程还能接受外部的任务并且运行;

JDK中的线程池和工作原理
ThreadPoolExecutor ,jdk所有线程池实现的父类

各个参数的含义
int corePoolSize, 核心线程数, < corePoolSize ,就会创建新线程 = corePoolSize , 保存到 BlockingQueue (int prestartAllCoreThreads() 调用这个方法 ,就会一次性创建 corePoolSize 的 线程 )
int maximumPoolSize, 允许的最大线程数,BlockingQueue 满了, 但是小于 maxmumPoolSize 的时候 就会创建新的线程
long keepAliveTime, 存活时间,在 线程数 > corePoolSize 的时候,空闲时间超过 keepAliveTime 就会销毁
TimeUnit unit, 存活时间的单位
BlockingQueue workQueue, 保存任务的阻塞队列
ThreadFactory threadFactory, 线程工厂,给新建的线程赋名字
RejectedExecutionHandler handler 饱和策略

AbortPolicy 直接抛出异常,默认

CallerRunsPolicy 用调用者所在的线程来执行任务;谁提交的任务谁执行

DiscardOldestPolicy 丢弃阻塞队列里最老的任务,队列里最靠前的任务

DiscardPolicy 丢弃当前任务

提交任务
void execute(Runnable command) 无返回

Future submit(Runnable task, T result) 需要返回

工作机制

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        / *
          *3步进行:
          *
          * 1.如果少于正在运行的corePoolSize线程,尝试
          *以给定命令作为第一个线程来启动新线程
          *任务。 对addWorker的调用自动检查runState和
          * workerCount,因此可以防止增加误报
          *通过返回false而不应该进行线程化。
          *
          * 2.如果一个任务可以成功排队,那么我们仍然需要
          *仔细检查我们是否应该添加线程
          *(因为现有的自上次检查后死亡)或
          *自从进入此方法以来,该池已关闭。 所以我们
          *重新检查状态,并在必要时回退排队
          *停止,如果没有,则启动一个新线程。
          *
          * 3.如果我们无法将任务排队,那么我们尝试添加一个新的
          *线程。 如果失败,我们知道我们已经关闭或饱和
          *并因此拒绝任务。
          * /
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

关闭线程池
void shutdown();设置线程池的状态,只会中断所有没有执行任务的线程

List shutdownNow(); 设置线程池的状态,还会去尝试停止正在运行或者暂停任务的线程;

java里的线程 协作式的,不是抢占式,调用了 shutdownNow 也不一定马山停止

合理配置线程池
根据任务的性质:计算密集型(CPU密集型)、IO密集型、混合型;

计算密集型 : 加密,大数分解,正则

线程数适当小一点、最大推荐:CPU核心数+1;

为什么+1 ?

防止 页缺失(需要执行的数据应该全部加载都内存中,但是因为一些原因某些数据还在磁盘上,操作系统需要把这些数据调度到内存中,这种情况叫做页缺失)

为什么只能+1?

因为计算密集型必须要把CPU充分利用起来,线程多了,就会产生上下文切换的消耗

Runtime.getRuntime().availableProcessors() 这个方法可以获取当前机器的核心数;

IO密集型 : 读取文件、数据库连接、 网络通信、rpc;

线程数适当大一点 、推荐:机器CPU的核心数*2;

上线以后根据业务实际情况调整 (用户态、系统态)

混合型 : 既有 计算密集型 又有 IO密集型

尽量拆分,

IO密集型 >> 计算密集型, 拆分意义不大

队列的选择上 : 应该使用有界(拒绝可以重新提交执行),无界队列可能会导致内存溢出 OOM

系统为我们预定义的线程池详解
FixedThreadPool
corePoolSize == maximumPoolSize

创建固定线程数量的,适用于 负载较重的服务器,使用了无界队列

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

SingleThreadExecutor
创建单个线程,需要保证顺序执行任务,不会有多个线程启动,使用了无界队列;

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

CachedThreadPool
会根据需要来创建新线程,执行很多短期异步任务的程序,使用了 SynchronousQueue, 任务量新增速度大于任务处理速度时,很容易崩

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

SingleThreadScheduledExecutor
单线程执行周期任务

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }

ScheduledThreadPool
相同作用:Timer(问题多,抛出异常整个停止)

适用于定期执行任务, 可控制后台线程数量

为了保证程序健壮性,可将异常捕捉;如不捕获,抛出异常,任务将停止;

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

建议提交给 ScheduledThreadPool 执行的任务要 catch 异常;免得任务停止;

方法
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) ; 只执行一次,任务还可以延时执行;

ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) ;提交固定时间间隔的任务(开始间隔时间相等)

ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) ;提交固定延迟时间间隔执行的任务

(结束时间和下次开始时间间隔相等)

scheduleAtFixedRate 任务超时:

60s执行一次,有任务执行了80s,下一个任务马上开始执行;

第一个任务80s,第二个任务20s,第三个任务50s;

第一个任务 0s 开始 ,80s结束,第二个任务 80s 开始,100s 结束; 第三个任务 120s 开始;第四个任务 180s 开始;

Executor框架
在这里插入图片描述

基本使用流程
在这里插入图片描述

实际开发中,通常会 new ThreadPoolExecutor(), 因为预定义的线程池都使用了无界队列,

拒绝策略,通常需自定义;

CompletionService
先完成的任务先返回

//完成的任务才会被放进去 BlockingQueue

public class ExecutorCompletionService<V> implements CompletionService<V> {
    private final Executor executor;
    private final AbstractExecutorService aes;
    //完成的任务才会被放进去  BlockingQueue
    private final BlockingQueue<Future<V>> completionQueue;

    /**
     * FutureTask extension to enqueue upon completion
     */
    private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

    private RunnableFuture<V> newTaskFor(Callable<V> task) {
        if (aes == null)
            return new FutureTask<V>(task);
        else
            return aes.newTaskFor(task);
    }

    private RunnableFuture<V> newTaskFor(Runnable task, V result) {
        if (aes == null)
            return new FutureTask<V>(task, result);
        else
            return aes.newTaskFor(task, result);
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值