ThreadPool

ThreadPool

基于jdk1.8

1、线程池参数

ThreadPoolExecutor构造函数

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

​ 参数说明:

  • corePoolSize: 线程池核心线程数
  • maximumPoolSize: 线程池最大数量
  • keepAliveTime: 线程空闲时间
  • unit: 时间单位
  • workQueue: 线程池所使用的缓冲队列
  • threadFactory: 线程池创建线程使用的工厂
  • handler: 线程池拒绝任务处理策略

2、阻塞队列

​ BlockingQueue是juc包下一种数据结构,提供线程安全的访问控制。

几种常用阻塞队列

1、ArrayBlockingQueue
public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

​ 基于数组的有界阻塞队列。
​ capacity:初始化队列长度;
​ fair:队列是否满足FIFO,默认为false

2、DelayQueue

​ 无界有序的BlockingQueue,用于存放实现了Delayed接口的元素。队头元素肯定是最先到期的。底层基于PriorityQueue实现。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {
    private final PriorityQueue<E> q = new PriorityQueue<E>();
}

​ 实现Delayed接口需要重写 compareTo 和 getDelay 两个方法

new Delayed() {
    // 用于在PriorityQueue中排序
    @Override
    public int compareTo(Delayed o) {
        return 0;
    }

    // 获取延迟时间
    @Override
    public long getDelay(TimeUnit unit) {
        return 0;
    }
};

​ 如何实现排序和延迟?
​ 一、排序
​ 往队列中添加元素会调用 PriorityQueue 的 offer方法,DelayQueue 在初始化 PriorityQueue 是没有指定 Comparator,则会走siftUpComparable(int k, E x) 方法,但由于元素实现了 Delayed 接口的 compareTo 方法,所以在添加元素的时候会进行排序。

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}

private void siftUp(int k, E x) {
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

@SuppressWarnings("unchecked")
private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (key.compareTo((E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = key;
}

@SuppressWarnings("unchecked")
private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

​ 二、延迟
​ 取数据是会调用getDelay方法,判断元素是否已到期

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        E first = q.peek();
        if (first == null || first.getDelay(NANOSECONDS) > 0)
            return null;
        else
            return q.poll();
    } finally {
        lock.unlock();
    }
}
3、LinkedBlockingQueue

​ 基于链表实现的阻塞队列,初始化是可以指定初始长度,不指定默认长度为 Integer.MAX_VALUE

    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity} is not greater
     *         than zero
     */
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

    /**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}, initially containing the elements of the
     * given collection,
     * added in traversal order of the collection's iterator.
     *
     * @param c the collection of elements to initially contain
     * @throws NullPointerException if the specified collection or any
     *         of its elements are null
     */
    public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }
4、PriorityBlockingQueue

​ 无界的优先级阻塞队列,内部基数数组实现,优先级排序与PriorityQueue类似

    /**
     * Creates a {@code PriorityBlockingQueue} with the specified
     * initial capacity that orders its elements according to their
     * {@linkplain Comparable natural ordering}.
     *
     * @param initialCapacity the initial capacity for this priority queue
     * @throws IllegalArgumentException if {@code initialCapacity} is less
     *         than 1
     */
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

    /**
     * Creates a {@code PriorityBlockingQueue} with the specified initial
     * capacity that orders its elements according to the specified
     * comparator.
     *
     * @param initialCapacity the initial capacity for this priority queue
     * @param  comparator the comparator that will be used to order this
     *         priority queue.  If {@code null}, the {@linkplain Comparable
     *         natural ordering} of the elements will be used.
     * @throws IllegalArgumentException if {@code initialCapacity} is less
     *         than 1
     */
    public PriorityBlockingQueue(int initialCapacity,
                                 Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.comparator = comparator;
        this.queue = new Object[initialCapacity];
    }
5、SynchronousQueue

​ 同步阻塞队列。

    /**
     * Creates a {@code SynchronousQueue} with nonfair access policy.
     */
    public SynchronousQueue() {
        this(false);
    }

    /**
     * Creates a {@code SynchronousQueue} with the specified fairness policy.
     *
     * @param fair if true, waiting threads contend in FIFO order for
     *        access; otherwise the order is unspecified.
     */
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

​ 如果调用put往队列中添加元素,则内部同时只能容纳单个元素,如果队列中已有一个元素再往队列中put会被阻塞;同时取数据时如果队列为空也会一直阻塞直到有数据插入。

@Test
public void test1() throws InterruptedException {

    // 定义一个SynchronousQueue 默认为非公平队列
    SynchronousQueue<Integer> queue = new SynchronousQueue<>();

    // 插入数据线程
    Thread putThread = new Thread(() -> {
        try {
            for (int i = 0; i < 3; i++) {
                System.out.println("++++++++ begin put item: " + i);

                queue.put(i);

                System.out.println("++++++++ item: " + i + " put end");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

	// 消费数据线程
    Thread takeThread = new Thread(() -> {
        try {
            for (int i = 0; i < 3; i++) {
                System.out.println("------- begin talk");

                Integer result = queue.take();

                System.out.println("------- end take result: " + result);

                // 每次取完sleep 1s
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    putThread.start();
    // 取线程延迟1s开始
    Thread.sleep(1000);
    takeThread.start();


    Thread.sleep(10000);
}

​ 运行结果

++++++++ begin put item: 0
------- begin talk
------- end take result: 0
++++++++ item: 0 put end
++++++++ begin put item: 1
------- begin talk
------- end take result: 1
++++++++ item: 1 put end
++++++++ begin put item: 2
------- begin talk
------- end take result: 2
++++++++ item: 2 put end

注意1: 它一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。
注意2: 它是线程安全的,是阻塞的。
注意3: 不允许使用 null 元素。
注意4: 公平排序策略是指调用put的线程之间,或take的线程之间。公平排序策略可以查考ArrayBlockingQueue中的公平策略。
注意5: SynchronousQueue的以下方法:

  • iterator() 永远返回空,因为里面没东西。
  • peek() 永远返回null。
  • put() 往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走。
  • offer() 往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false。
  • offer(2000, TimeUnit.SECONDS) 往queue里放一个element但是等待指定的时间后才返回,返回的逻辑和offer()方法一样。
  • take() 取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。
  • poll() 取出并且remove掉queue里的element(认为是在queue里的。。。),只有到碰巧另外一个线程正在往queue里offer数据或者put数据的时候,该方法才会取到东西。否则立即返回null。
  • poll(2000, TimeUnit.SECONDS) 等待指定的时间然后取出并且remove掉queue里的element,其实就是再等其他的thread来往里塞。
  • isEmpty()永远是true。
  • remainingCapacity() 永远是0。
  • remove()和removeAll() 永远是false。

3、拒绝策略

​ RejectedExecutionHandler 有四种拒绝策略

  • ThreadPoolExecutor.AbortPolicy(中止策略)

    功能: 使用时直接抛出RejectedExecutionException

    注意: ThreadPoolExecutor默认的拒绝策略就是AbortPolicy,但是如果使用Executors创建的newFixedThreadPool和newSingleThreadExecutor由于底层使用了LinkedBlockingQueue(无界队列),导致一直不会触发此中止策略最后出现oom。

  • ThreadPoolExecutor.DiscardPolicy(丢弃策略)

    功能: 直接丢弃这个任务,不做任何处理

    使用场景: 如果提交的任务无关紧要可以使用此策略。因为这个策略是个空实现,不会做任何处理所以基本不使用。

  • ThreadPoolExcutor.DiscardOldestPolicy(丢弃老任务策略)

    功能: 丢弃队列头部数据,并执行

  • ThreadPoolExecutor.CallerRunsPolicy (调用者执行策略)

    功能: 由提交任务的当前线程执行


4、ExcutorService 的四种线程池(不推荐使用)

/*
	创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
	底层分析:corePoolSize为0;maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为60L;unit为TimeUnit.SECONDS;workQueue为SynchronousQueue(同步队列)
	使用场景:执行很多短期异步的小程序或者负载较轻的服务器
	通俗解释来说就是:当有新任务到来,则插入到SynchronousQueue中,由于SynchronousQueue是同步队列,因此会在池中寻找可用线程来执行,若有可以线程则执行,若没有可用线程则创建一个线程来执行该任务;若池中线程空闲时间超过指定大小,则该线程会被销毁。
*/
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

/*
	创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
	底层分析:返回ThreadPoolExecutor实例,接收参数为所设定线程数量nThread,corePoolSize为nThread,maximumPoolSize为nThread;keepAliveTime为0L(不限时);unit为:TimeUnit.MILLISECONDS;WorkQueue为:new LinkedBlockingQueue<Runnable>() 无界阻塞队列
	使用场景:执行长期的任务,性能好很多
	通俗地来说:创建可容纳固定数量线程的池子,每隔线程的存活时间是无限的,当池子满了就不在添加线程了;如果池中的所有线程均在繁忙状态,对于新任务会进入阻塞队列中(无界的阻塞队列)
*/
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

/*
	创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
	底层分析:FinalizableDelegatedExecutorService包装的ThreadPoolExecutor实例,corePoolSize为1;maximumPoolSize为1;keepAliveTime为0L;unit为:TimeUnit.MILLISECONDS;workQueue为:new LinkedBlockingQueue<Runnable>() 无界阻塞队列
	使用场景:一个任务一个任务执行的场景
	通俗地来说:创建只有一个线程的线程池,且线程的存活时间是无限的;当该线程正繁忙时,对于新任务会进入阻塞队列中(无界的阻塞队列)
*/
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

/*
	创建一个定长线程池,支持定时和周期性任务执行
	底层分析:创建ScheduledThreadPoolExecutor实例,corePoolSize为传递来的参数,maximumPoolSize为Integer.MAX_VALUE;keepAliveTime为0;unit为:TimeUnit.NANOSECONDS;workQueue为:new DelayedWorkQueue() 一个按超时时间升序排序的队列
	使用场景:周期性执行任务的场景
	通俗地来说:创建一个固定大小的线程池,线程池内线程存活时间无限制,线程池可以支持定时及周期性任务执行,如果所有线程均处于繁忙状态,对于新任务会进入DelayedWorkQueue队列中,这是一种按照超时时间排序的队列结构
*/
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);

阿里巴巴Java 开发手册中有一条

【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:

1) FixedThreadPool SingleThreadPool
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。


5、推荐创建线程池方式

    /**
     * 普通线程池
     *
     * @return ThreadPoolExecutor
     */
    @Bean
    public ThreadPoolExecutor threadPool() {
        int coreThreadNum = Runtime.getRuntime().availableProcessors();
        int maxThreadNum = coreThreadNum * 2;
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("ocean_thread").build();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        return new ThreadPoolExecutor(coreThreadNum, maxThreadNum, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100, true), threadFactory, handler);
    }

    /**
     * 延迟线程池
     *
     * @return ScheduledExecutorService
     */
    @Bean
    public ScheduledExecutorService scheduledThreadPoolExecutor() {
        int coreThreadNum = Runtime.getRuntime().availableProcessors();
        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("ocean_scheduled_thread").build();
        return new ScheduledThreadPoolExecutor(coreThreadNum, threadFactory, new ThreadPoolExecutor.CallerRunsPolicy());

    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值