文档章节

JDK并发包 - 线程池

i
 itititit
发布于 2017/08/17 17:01
字数 1610
阅读 3
收藏 0
  • 过多的线程会耗尽CPU和内存资源,虽然与进程相比,线程是一种轻量级的工具,但是其创建和关闭依然需要花费时间。
  • 线程本身会占用内存空间,大量的线程会抢占宝贵的内存资源。大量的线程回收也会给GC带来压力,延长GC的停顿时间。
  • 线程池可以让线程复用,可以节约不少创建和销毁线程的时间。

Executor 框架

ThreadPoolExecutor:表示线程池。

Executors:扮演线程池工厂的角色,通过Executors 可以取得拥有特定功能的线程池。

public static ExecutorService newFixedThreadPool(int nThreads)

返回固定线程数量的线程池。线程池中的数量始终不变,若没有空闲的线程,则新的线程会被暂存在一个额任务列表中。 

public static ExecutorService newSingleThreadExecutor()

返回只有一个线程的线程池,多余的任务被暂存到任务列表中,按先进先出的顺序执行。 

public static ExecutorService newCachedThreadPool()
  •  该线程池可以显著提高大量短暂的异步任务的性能(many short-lived asynchronous tasks)。
  • 如果没有可用的线程,则新建线程加入线程池。
  • 如果空闲60秒,会被移除线程池。
public static ScheduledExecutorService newSingleThreadScheduledExecutor()

 返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService 接口在ExecutorServiec之上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

也返回ScheduledExecutorService 对象, 但该线程可以指定线程数量。

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

long initialDelay: 第一次执行的延迟时间。

long delay: 上一个任务结束后延迟执行下一个任务的时间。

  • schedule()会在给定的时间,对任务进行一次调度。
  • scheduleFixedRate() 任务频率是一定的,是以上一个任务开始执行时间为起点,调度下一次任务,如果任务的执行时间超过了调度时间,下个任务会立即被调用
  • scheduleWithFixedDelay() 是在上一个任务结束时间为起点,经过delay时间进行任务调度。

遇到异常,那么后续的所有任务都会停止调度,因此,必须保证异常的及时处理,为周期性的任务的稳定调度提供条件。

 

 

线程池的内部实现:

上述的几种线程池有着不同的功能特点,但其内部实现都是使用ThreadPoolExecutor实现。

public ThreadPoolExecutor(
            int corePoolSize,
            int maximumPoolSize,
            long keepAliveTime,
            TimeUnit unit,
            BlockingQueue<Runnable> workQueue,
            ThreadFactory threadFactory,
            RejectedExecutionHandler handler);
  • corePoolSize:线程池中的线程数量。
  • maximumPoolSize:线程池中的最大线程数量。
  • keepAliveTime:当线程数超过corePoolSize时,多余的空闲线程的存活时间。
  • unit:keepAliveTime的时间单位。
  • workQueue:任务队列,被提交但尚未被执行的任务(等待有空闲的线程)。
  • threadFactory: 线程工厂,用于线程的创建,一般用默认的即可。
  • handler:拒绝策略。当任务太多来不及处理,如何拒绝任务。

任务队列:workQueue

workQueue是一个BlockingQueue 接口的对象,仅用于存放Runnable对象。

  • SynchronousQueue:直接提交的队列,没有容量!每一个插入操作都要等待一个相应的删除操作,反之,每一个删除操作都要等待对应的插入操作。提交的任务不会被真实的保存,而总是将新任务提交给线程执行,如果没有空闲的线程,则尝试创建线程,如果线程数量已经达到最大值,则执行拒绝策略
  • ArrayBlockingQueue:有界的任务队列。构造参数必须带一个容量参数。如果线程池的实际线程小于corePoolSize,则优先创建线程,若大于corePoolSoize,则会将新任务加入等待队列。若等待队列已满,则在线程池总线程数不大于maximumPoolSize的前提下,创建新线程,否则执行拒绝策略
  • LinkedBlockingQueue:无界的任务队列。线程数小于corePoolSize,创建新的线程执行。当达到corePoolSize后,不会继续增加,若后续的任务加入,且没有空闲的线程,则任务直接加入队列等待。若加入任务和处理任务的差异很大,队列会快速增长,直到内存耗尽。
  • PriorityBlockingQueue:优先任务队列,是一个特殊的无界队列总是确保高优先级的任务先执行

拒绝策略

  • AbortPolicy:直接抛出异常,阻止系统正常工作。
  • CallerRunsPolicy:只要线程池未关闭,任务直接在调用这线程中执行,不会丢弃任务,但是,提交任务的线程的性能可能急剧下降。
  • DiscardOledestPolicy:丢弃最老的请求,并尝试再次提交当前任务。
  • DiscardPolicy:默默地丢弃无法处理的任务。

自定义线程创建:ThreadFactory

线程池中的线程都是通过 接口 ThreadFactor接口的newThread()创建的。默认实现为Executors.DefaultThreadFactory。

通过自定义线程池,可以跟踪线程在何时创建了多少线程,也可以自定义线程的名称、组以及优先级等信息。

扩展线程池

通过扩展ThreadPoolExecutor,重写其 beforeExecute()、afterExecute()、terminated()三个接口,可以监控任务执行的开始和结束时间,或者其他一些自定义的增强功能。

合理的线程池线程数量

线程池的大小对系统的性能有一定的影响。过大或过下的线程数量无法发挥最优的系统性能,但是线程池大小的确认也不需要做到非常精确,只要避免极大和极小两种情况。

Nthreads = Ncpu * Ucpu * ( 1 + W /  C )

  • Ncpu = CPU的数量
  • Ucpu = 目标CPU的使用率
  • W/C = 等待时间/计算时间的比率 ???

线程异常处理

可通过重写ThreadPoolExecutor的executre() 方法,在异常捕获中打打印任务提交线程的堆栈信息。

   class TraceThreadPoolExecutor extends ThreadPoolExecutor{
        public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        @Override
        public void execute(Runnable command) {
            super.execute(exceptionWrap(command, new Exception(""),Thread.currentThread().getName()));
        }

        @Override
        public Future<?> submit(Runnable task) {
           return super.submit(exceptionWrap(task, new Exception(""),Thread.currentThread().getName()));
        }
    }

    private Runnable exceptionWrap(final Runnable task, final Exception invokerStack,String invokerThreadName){
        return new Runnable() {
            @Override
            public void run() {
                try {
                    task.run();
                }catch ( Exception e){
                    invokerStack.printStackTrace(); // 打印调用这堆栈信息
                    throw e;
                }
            }
        };
    }

 

© 著作权归作者所有

i
粉丝 0
博文 24
码字总数 15531
作品 0
私信 提问
[高并发Java 六] JDK并发包2

线程池的基本使用 1.1.为什么需要线程池 平时的业务中,如果要使用多线程,那么我们会在业务开始前创建线程,业务结束后,销毁线程。但是对于业务来说,线程的创建和销毁是与业务本身无关的,...

Hosee
2016/02/08
5.3K
4
zxpdt@163.com/ConcurrentLatch

ConcurrentLatch ConcurrentLatch是一个基于JDK的多线程归类并发处理闩工具 ConcurrentLatch使用场景 当你有5个无相关性操作,顺序执行那么消耗时间合将达到5个操作的所有操作时间和,如果开...

zxpdt@163.com
2017/10/26
0
0
Java 并发工具包 | J.U.C

Java 并发工具包 | J.U.C 不知道大家还有没有印象,上次我们已经说过了,我们为了实现集合相关类的线程安全,JDK 提供了一套同步容器,也就是 Vector,Hashtable,还有一个 Collections 工具...

优惠码领取
03/14
0
0
Java 并发编程-不懂原理多吃亏(送书福利)

作者 加多 关注阿里巴巴云原生公众号,后台回复关键字“并发”,即可参与送书抽奖!** 导读:并发编程与 Java 中其他知识点相比较而言学习门槛较高,从而导致很多人望而却步。但无论是职场面...

阿里巴巴云原生
08/30
95
0
浅析ConcurrentHashMap

一、导论 这些天一直在看关于多线程和高并发的书籍,也对jdk中的并发措施了解了些许,看到concurrentHashMap的时候感觉知识点很乱,有必要写篇博客整理记录一下。 当资源在多线程下共享时会产...

bxst
2017/07/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

喜玛拉雅已听书单

时间倒序排 书名 作者 状态 唐砖 孑与2 进行中 死灵之书(克苏鲁神话合集) 阿卜杜拉·阿尔哈萨德 进行中 赡养人类 刘慈欣 完结 赡养上帝 刘慈欣 完结 中国太阳 刘慈欣 完结 中国太阳 刘慈欣...

Alex_Java
29分钟前
5
0
Java8新特性时间日期库DateTime API及示例

Java8新特性的功能已经更新了不少篇幅了,今天重点讲解时间日期库中DateTime相关处理。同样的,如果你现在依旧在项目中使用传统Date、Calendar和SimpleDateFormat等API来处理日期相关操作,这...

程序新视界
39分钟前
7
0
医疗项目pc端后台页面用vue重构整个html页面

页面源码是vue

xintaiideas
今天
6
0
领域驱动中的“贫血症和失忆症” --实践领域驱动--原文

贫血症严重危害着人类健康,并且伴随有危险的副作用。当贫血领域对象被首次提出来时,它并不是一个博得赞美的词汇,它描述的是一个缺少内在行为领域对象。奇怪的是,人们对于贫血领域对象的态...

还仙
今天
6
0
条码打印软件中标签预览正常打印无反应怎么解决

在使用条码打印软件制作标签时,有客户反馈,标签打印预览正常的,但是打印无反应,咨询是怎么回事?今天针对这个情况,可以参考以下方法进行解决。 一、预览正常情况下,打印没反应 (1)在条码...

中琅软件
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部