JAVA线程池

ThreadPoolExecutor

先看一下他的最全参数的一个构造器

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize: 核心线程数,线程池中保留的线程数,除非设置了 allowCoreThreadTimeOut ,不然核心线程会一直存活,即使他们处于空闲状态
maximumPoolSize: 最大线程数,线程池中允许的最大线程数
keeAliveTime: 存活时间, 线程的最大空闲时间,如果超过这个时间线程将被回收
TimeUnit: 存活时间的 时间单位
workQueue: 任务阻塞队列,当任务超过核心线程数了,就会把任务储存到任务队列
threadFactory: 线程工厂
handler: 拒绝策略
AbortPolicy:

AbortPolicy线程池的默认策略,如果任务添加失败 抛异常RejectedExecutionException
DiscardPolicy任务添加失败, 什么都不做
DiscardOldestPolicy丢弃最早添加的任务,重新执行该任务
CallerRunsPolicy直接在调用线程中,运行该线程
线程池执行流程

线程池执行流程

有界,无界队列
  1. 有界队列
    有界: 队列的容量有大小限定;
	// 一个有界阻塞队列,必须指定队列的大小
        ArrayBlockingQueue abq = new ArrayBlockingQueue(10);
       //  也是一个有界队列, 如果不指定队列大小默认实Integer.Max_VALUE
        LinkedBlockingQueue lbq = new LinkedBlockingQueue(10);
          public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }
//	     一个不存储元素的队列,队列的大小为0,添加一个元素时必须等待另一个线程获取出这个元素
		SynchronousQueue shq = new SynchronousQueue();
  1. 无界队列

队列的大小不受限制;可以无限添加元素

// 一个线程安全的 非阻塞无界队列;
 ConcurrentLinkedQueue concurrentLinkedQueue = new ConcurrentLinkedQueue();
 // 具有排序的优先级的 阻塞队列
  PriorityBlockingQueue priorityBlockingQueue = new PriorityBlockingQueue();
  //DelayQueue是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
  DelayQueue delayedQueue = new DelayQueue();
  1. 阻塞队列
    阻塞队列都实现了BlockingQueue 接口, 在进行元素操作的时候会加锁 阻塞;
JDK中提供的线程池
 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

Executors.newFixedThreadPool(2); 指定了核心线程数和 最大线程数都是nThreads
选用的任务队列是

new LinkedBlockingQueue<Runnable>()
...
// 它的默认构造器 容量大小是 Integer.MAX_VALUE
  public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

这种方式创建线程池 他的核心大小和最大线程数是一样的, 线程的空闲时间为0,
队列是个 有界(Integer最大值)阻塞队列LinkedBlockingQueue,拒绝策略 默认的AbortPolicy
根据线程池的执行流程,这样创建的线程当任务操作核心线程数的时候会一直往队列中塞任务, 队列大小Integer.MAX_VALUE 最终导致内存溢出, 而且默认的拒绝策略也不是程序中想要的

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

newSingleThreadExecutor 创建一个只有1个核心线程数和 最大线程数的单一线程池;就是说 使用此线程池 所有的任务 只有一个线程在处理,超过的就直接扔进队列

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

newCachedThreadPool, 可缓存的线程池;核心线程数 0, 最大线程数 Integer.MAX_VALUE,存活时间 60s, 队列 使用的是 SynchronousQueue, 这个队列是不储存元素的队列,就是说添加一个元素必须等待另一个线程获取这个元素; 这样创建的线程池执行机制就是 会随着任务增多 不断的创建线程执行任务, 最后内存溢出 或cpu卡死。
由于线程的存活时间是60S ,所以当第一个任务执行完毕后线程还存活, 后续的任务会复用 该线程

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

定时或周期性的执行任务
newScheduledThreadPool 方式创建线程池,核心线程数 我们可以自己指定,由于最大线程数 Integer.MAX_VALE,所以在一定情况下还是会线程过多 系统卡死

阿里巴巴手册中的规定

. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样
的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool :
允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :
允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。

总结: 如果我们熟悉了线程池的各个参数,最好能自己定义线程池,而不要使用默认提供的,甚至要自己指定拒绝策略和线程池工厂

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值