JDK并发包 - 线程池

  • 过多的线程会耗尽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;
                }
            }
        };
    }

 

转载于:https://my.oschina.net/u/3171491/blog/1512246

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值