栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java线程池详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java线程池详解

  • 1.Java线程池类图:
  • 2.线程池状态
    • ThreadPoolExecutor使用int的高3位来表示线程池状态,低29位来表示线程数量
状态名高3位接收新任务处理阻塞任务队列说明
RUNNING111YY
SHUTDOWN000NY不会接收新任务,但会处理阻塞队列剩余任务
STOP001NN
TIDYING010--
TERMINATED011--
  • SHUTDOWN:是指调用了线程池的shutdown()方法,此时意图停止线程池,它是一种比较温和的,正在执行的任务和阻塞队列中的任务,都不会取消掉,等这些任务都被处理掉后,线程池才会关闭,但是调用了shutdown()以后,就不会再接收新任务了
  • STOP:是指调用了线程池中的shutdownNow()方法,暴力停止线程池,不会接收新任务,正在执行的任务也会被打断(通过调用interrupt()来打断),阻塞队列的任务也会被抛弃掉
  • TIDYING:是一个过渡状态,当任务全部执行完毕,活动的线程也为0了,线程池就处于TIDYING,线程池即将进入TERMINATED状态
  • TERMINATED:线程池已经不能工作了,处于中介状态

从数字上比较:TERMINATED > TIDYING > STOP > SHUTDOWN > RUNNING(RUNNING为负数,因为是高三位,最高位为符号位)

为了保证状态信息 以及 线程数量在赋值时的原子性;他们被存储在一个原子变量ctl中,目的是将线程池状态与线程个数合二为一,这样就可以用一次cas原子操作进行赋值

// c为旧值,ctlOf返回结果为新值
ctl.compareAndSet(c, ctlOf(targetState, workCountOf(c)));
// rs为高3位,代表线程池状态,wc为低29位代表线程个数,ctlOf是合并他们
private static int ctlOf(int rs, int wc){return rs | wc;}
  • 3.Java线程池构造方法
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
  • corePoolSize:核心线程数(最多保留的线程数)
  • maximumPoolSize:最大线程数目
  • keepAliveTime:生存时间-针对救急线程
  • unit:时间单位-针对救急线程生存时间
  • workQueue:阻塞队列
  • threadFactory:线程工厂,可以为线程创建时起名字
  • handler:拒绝策略

线程池中的线程是采用懒加载机制的,当需要用到线程时才创建,并不是一开始就创建好所有的线程

  • 1.创建线程池,并设置相应的参数
  • 2.当来了一个任务,线程池中会创建一个核心线程来执行任务
  • 3.后边又来了多个任务,当所有的核心线程都在执行任务,即核心线程数达到corePoolSize时,新来的任务会被放入workQueue,即阻塞队列
  • 4.当阻塞队列被放满后,又来了新的任务,那么就会开始创建救急线程,执行新来的任务(注意救急线程处理的是阻塞队列放不下的任务)
  • 5.救急线程执行完任务后,他会有一个生存时间,如果是到时间了,依旧没有任务给救急线程执行,那么救急线程就会被销毁掉
  • 6.如果救急线程也达到最大创建数目,即核心线程数 + 救急线程数 = maximumPoolSize时,新来的任务就会执行拒绝策略

注意:核心线程被创建后,即使后期没有任务,它也会被保留在线程池中,不会被回收,会一直运行
如果阻塞队列选择了有界队列,那么任务数目超过了阻塞队列大小时,会创建救急线程;如果选择的是无界队列,那么就不存在救急线程的概念;即救急线程是配合有界阻塞队列来使用

JDK提供了4中拒绝策略的实现:(abort: 中断、终止 reject:拒绝 discard:抛弃、丢弃)

  • 1.AbortPolicy:让调用者抛出RejectedExecutionException异常,这是默认策略

  • 2.CallerRunsPolicy:让调用者运行任务

  • 3.DiscardPolicy:放弃本次任务

  • 4.DiscardOldestPolicy:放弃队列中最早的任务,本任务取而代之

  • RPC框架Dubbo的实现:在抛出RejectedExecutionException异常之前会记录日志,并dump线程栈信息,方便定位

  • 通信框架 Netty的实现,是创建一个新线程来执行任务

  • ActiveMQ的实现:加一个超时等待(60s),时间内会不断尝试放入队列

  • PinPoint的实现,它使用了一个拒绝策略量,会逐一尝试策略链中的每一种拒绝策略

  • 但是ThreadPoolExecutor类并不容易掌握,JDK就提供了一个Executors类,该类中提供了众多工厂方法来创建各种用途的线程池

  • 4.Executors

    • 4.1.newFixedThreadPool:创建固定大小的线程池
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);

特点:没有救急线程,因此也无需超时时间
阻塞队列是无界的,可以放任意数量的任务
适用于任务量已知,不是特别繁忙,不会一次性来大量的任务,相对耗时的任务

  • 4.2.NewCachedThreadPool
    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);
    }

特点:

  • 核心线程数为0,即全都是救急线程,maximumPoolSize = Integer.MAX_VALUE; 救急线程的空闲生存时间是60s,救急线程可以无限创建

  • 队列采用了SynchronousQueue实现,它没有容量,没有线程来取是放不进去的(一手交钱、一手交货)

  • 整个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲1分钟后释放线程

  • 适用于任务数比较密集,但每个任务执行时间比较短的情况

  • 4.3.newSingleThreadExecutor

    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));
    }
  • 使用场景:希望多个任务排队执行,线程数固定为1,任务数多于1时,会放入无界队列排队。任务执行完毕,这个唯一的线程也不会释放

  • 区别:如果是自己创建一个单线程串行执行任务,如果任务执行失败而终止,那么没有任何补救措施,而线程池中即使之前的线程由于意外结束了,还会新建一个线程,保证线程池的正常工作

  • Executor.newSingleThreadExecution()线程数为1,不能修改

    • FinalizableDelegatedExecutorService应用的是装饰器模式,只对外暴露了ExecutorService接口,因此不能调用ThreadPoolExecutor中特有的方法
  • Executors.newFixedThread(1)初始时为1,以后还可以修改,对外暴露的是ThreadPoolExecutor对象,可以强制转换调用setCorePoolSize等方法进行修改

  • 5.线程池中的方法

// 执行某个任务
void execute(Runnable command)
 
// 提交任务task,并返回值Future获得任务执行结果
Future submit(Runnable task);
 Future submit(Callable task);
 Future submit(Runnable task, T result);

 void shutdown();
 void shutdowNow();

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/293364.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号