Java线程池和线程相关类

7 篇文章 0 订阅

前言

参考项目:https://github.com/wodongx123/ThreadPoolDemo

1.线程池

Java Pool(Java 池)是为了减少资源创建、初始化的系统开销而采取的一种实现模式。
线程池维护多个线程,可以有效控制系统中并发的线程数量。

  • 线程的创建和销毁的开销比较高,而线程池会在系统启动时即时创建好几个线程。
  • 程序中需要大量创建生存时间很短的线程时,可以使用线程池。
  • 线程池可以有效的控制程序内的线程数量。

1.1 线程池的种类

newCachedThreadPool()

创建一个有缓存功能的线程池,根据系统需要创建线程,这些线程会存放在缓存中。

  • 放入缓存的线程拥有复用性,可以一遍又一遍的拿出来跑程序。
  • 放入缓存的线程,在经过一段时间后如果没有用到(默认60s),就会自动销毁并且移出缓存。所以CachedThreadPool挂机长时间后,它不会占用系统资源。
  • 如果缓存内没有新线程,会自动创建一个新的。
  • 适合用来处理时间较短的异步任务。
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

newFixedThreadPool(int nThreads)

创建一个可重用的,具有固定线程数的线程池。
newSingleThreadExecutor相当于newFixedThreadPool(1).

  • 创建指定数量的线程在线程池内待机。
  • 重复完成任务。
  • 如果在完成任务的中途出错,会创建一个新的线程来代替它继续工作。
  • 线程不会随着时间的推移而被清除(也就是说除非被shutdown,不然这种线程池在创建的时候就会一直占用系统资源)。
  • 适合处理长期任务。
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

newScheduledThreadPool(int corePoolSize)

创建有指定线程数的线程池,可以在延迟指定时间后执行任务或者按周期执行任务,corePoolSize表示池中保存的线程数,不会随着空闲而被移除。
newSingleThreadScheduledExecutor()相当于newScheduledThreadPool(1)。

  • 通过ScheduledExecutorService创建。
  • 创建指定数量的线程在线程池内待机。
  • 重复的根据设定的延迟时间和周期时间完成任务。
  • 如果在完成任务的中途出错,会创建一个新的线程来代替它继续工作。
  • 线程不会因为空闲而被移除。
  • 适合处理周期性任务。
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
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);
  • schedule(): 延迟指定的时间delay后开始任务command。
  • scheduledAtFixedRate():延迟指定的时间initialDelay后,开始任务。从开始任务时开始计时,每经过period后重新开始任务(周期性执行);如果任务的执行时长taskTime大于period的话,每经过taskTime后重新开始任务。
  • scheduleWithFixedDelay():延迟指定的时间initialDelay后,开始任务。从任务结束时开始计时,每经过deley时长后重新开始任务。

newWorkStealingPool(int parallelism)

创建持有足够的线程池来支持给定的并行级别,使用多个队列来减少竞争。
stealing翻译是窃取,表示线程再完成自己的任务后会去主动的找任务来做。
newWorkStealingPool()根据CPU的核心数来设置并行等级,四核就是4,六核就是6。

  • JDK1.8以上才支持的功能。
  • 创建的线程池是后台线程,当主线程结束后就会立刻销毁。
  • 实际上的线程数不确定,会根据需求增加或减少,但是保证支持所需要的并行等级。
  • 不保证提交的任务按顺序执行(比如说任务按12345提交给线程池,但是线程池不一定会按12345来处理,有可能是54321,也有可能是别的顺序)。
  • 适合执行不需要按顺序执行的复杂任务
public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

1.2 ThreadPoolExecutor

上面的几个线程池除了newWorkStealingPool实际上到最后都是调用了ThreadPoolExecutor(),只是使用的参数不同。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
参数类型描述
corePoolSizeint核心线程池大小,也就是常驻线程的数量
maximumPoolSizeint最大线程池大小,所有线程(常驻线程+缓存线程)加起来的数量上限
keepAliveTimelong线程保活时间,缓存线程最长可以存在的时间,超过这个时间就被移除,为0表示无限时间
unitTimeUnit时间单元,对上一个参数的时间进行单位描述,比如xTimeUnit.SECONDS为秒
workQueueBlockingQueue任务队列
threadFactoryThreadFactory线程工厂,用于创建新线程的,是一个接口,内部就一个new方法
handlerRejectedExecutionHandler拒绝策略

1.3 ForkJoinPool

Java7开始额外增加的新的线程池,它支持将任务拆分成小任务,最后再合并成大任务模式。在Java8增加了通用池的功能。

  • 直接使用ForkJoinPool实例化。通用池使用commonPool调用,但是通用池不受shutdown影响。
  • 其中的线程是后台线程,如果主线程结束,会自动销毁。
  • 使用RecursiveAction(有返回值)和RecursiveTask来执行任务,两者均是extends,通过compute()方法写具体任务内容。
  • fork()会安排一个线程来执行指定的子任务。
  • join()会返回指定子任务的结果(如果是RecursiveAction就没有这个方法)。
public final ForkJoinTask<V> fork() {
    Thread t;
    if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread)
        ((ForkJoinWorkerThread)t).workQueue.push(this);
    else
        ForkJoinPool.common.externalPush(this);
    return this;
}

public final V join() {
    int s;
    if ((s = doJoin() & DONE_MASK) != NORMAL)
        reportException(s);
    return getRawResult();
}

2. 线程相关类

ThreadLocal

从另一个角度解决多线程冲突的类,不同于普通同步机制的加锁访问方式,ThreadLocal会将数据复制多份让每个线程都能访问数据。

  • 复制的数据不会影响到主线程的数据。
  • 主线程的数据不会影响到子线程的数据。
  • 通过initialValue设置默认值,没有重写的话就是null
  • 通过set和get的方法设置,获取值。

InheritableThreadLocal

和ThreadLocal大部分一样,但是这种情况下主线程的值可以传给子线程。

  • 子线程的数据还是无法影响主线程。

参考材料

疯狂JAVA讲义(第5版)
p768 - p779
线程池- 百度百科
https://baike.baidu.com/item/Java%20Pool/4715004?fr=aladdin#4
线程池的种类,区别和使用场景 - CNblog
https://www.cnblogs.com/sachen/p/7401959.html
可缓存线程池newCachedThreadPool - 简书
https://www.jianshu.com/p/4406c2aeff0f

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值