线程池和阻塞队列

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;
    }

参数

  • corePoolSize
    线程池中核心线程数量
  • maximumPoolSize
    线程池中最大线程数量
  • keepAliveTime
    线程池线程数超过核心线程时,空闲线程的存活时间
  • TimeUnit
    keepAliveTime的时间单位
  • workQueue
    任务队列,存放提交但尚未执行的任务
  • ThreadFactory
    线程工厂,用于创建线程
  • RejectedExecutionHandler
    拒绝策略,活动线程达到最大值且任务队列已满时或者其他线程池无法处理的任务的拒绝策略。

阻塞队列

  • ArrayBlockingQueue
    基于数组实现的有界阻塞队列,按照先进先出原则对队列中元素进行排序。默认情况下不保证元素操作的公平性。
public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }
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();
    }
public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);

        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }

//大小为1000的公平队列
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
//大小为1000的非公平队列
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,false);
  • LinkedBlockingQueue
    基于链表的有界阻塞队列,按照先进先出原则对元素进行排序,吞吐量通常高于ArrayBlockingQueue。
    在多线程对竞争资源的互斥访问时,对于插入和取出操作分别使用了不同的锁。对于插入操作,使用putLock进行同步,和非满条件notFull相关联;取出操作,使用takeLock进行同步,和非空条件notEmpty相关联。
//阻塞队列的大小,默认为Integer.MAX_VALUE
private final int capacity;
//当前阻塞队列中的元素个数
private final AtomicInteger count = new AtomicInteger();
//链表的表头,取出数据时,在表头head处取出
transient Node<E> head;
//链表表尾,新增数据时从表尾last处插入
private transient Node<E> last;
//取出元素时使用的锁
 private final ReentrantLock takeLock = new ReentrantLock();
//非空条件,当队列没有数据时用于挂起执行删除等操作的线程
 private final Condition notEmpty = takeLock.newCondition();
//添加元素时使用的锁
 private final ReentrantLock putLock = new ReentrantLock();
//非满条件,当队列已满时用于挂起执行添加操作的线程
 private final Condition notFull = putLock.newCondition();

·put
putLock和notFull条件搭配使用

public void put(E e) throws InterruptedException {
//如果添加为null,抛空指针异常
        if (e == null) throw new NullPointerException();
        int c = -1;
        //创建节点
        Node<E> node = new Node<E>(e);
        
        final ReentrantLock putLock = this.putLock;
        //队列中元素个数
        final AtomicInteger count = this.count;
        //加锁,可中断锁
        putLock.lockInterruptibly();
        try {
        //如果队列已满,等待
            while (count.get() == capacity) {
                notFull.await();
            }
            //如果队列没满,使用enqueue()在队尾加入元素
            enqueue(node);
            //队列长度+1,如果未满,使用signal唤醒其他阻塞队列
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
        //释放锁
            putLock.unlock();
        }
        if (c == 0)
        //如果队列中元素数量为0,唤醒notEmpty
            signalNotEmpty();
    }
private void enqueue(Node<E> node) {
//在队尾插入元素
        last = last.next = node;
    }
private void signalNotEmpty() {
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lock();
        try {
            notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
    }

·take()
takeLock和notEmpty条件搭配使用

public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        //加takeLock锁,可中断锁
        takeLock.lockInterruptibly();
        try {
        //如果队列为空,就一直等待
            while (count.get() == 0) {
                notEmpty.await();
            }
            //否则通过dequeue()取数据
            x = dequeue();
            //队列中元素数量-1
            c = count.getAndDecrement();
            //如果队列中剩余元素>1,唤醒其他阻塞线程
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        //如果队列已满,唤醒notFull
        if (c == capacity)
            signalNotFull();
        return x;
    }
  • PriorityBlockingQueue
    支持优先级排序的无界阻塞队列,元素在默认情况下使用自然顺序升序排序,可以自定义compareTo方法来指定元素进行排序规则,或者在初始化PriorityBlockingQueue时指定构造参数Comparator来实现对元素的排序。但如果两个元素优先级相同,则无法保证该元素的存储和访问顺序。

  • DelayQueue
    支持延时获取元素的无界阻塞队列。
    底层使用PriorityQueue实现。
    使用场景:
    缓存系统设计,使用DelayQueue保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从DelayQueue中获取元素,表示缓存的有效期到了。
    定时任务调度 ,使用DelayQueue保存即将执行的任务和执行时间,一旦从DelayQueue中获取元素,就表示任务开始执行

  • SynchronousQueue
    用于控制互斥操作的阻塞队列,每一个put操作都需要等待一个take操作完成,否则不能继续添加元素,适用于传递型场景,将在一个线程中使用的数据传递给另一个线程使用。

  • LinkedTransferQueue
    基于链表的无界阻塞队列

  • LinkedBlockingDeque
    基于链表的双向阻塞队列,可以在队列的两端分别执行插入和移除操作。在多线程同时操作队列时,减少一半的锁资源,提高了队列的操作效率

拒绝策略

  • AbortPolicy
    直接抛异常,阻止线程正常运行
public static class AbortPolicy implements RejectedExecutionHandler {
        public AbortPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
  • CallerRunsPolicy
    用调用者所在的线程来处理任务(比如说线程池线程数已满且任务队列已满,则把该任务回退给主线程执行)
 public static class CallerRunsPolicy implements RejectedExecutionHandler {
        public CallerRunsPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
  • DiscardOldestPolicy
    移除线程队列中最早的一个线程任务,并尝试提交当前任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public DiscardOldestPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }
  • DiscardPolicy
    丢弃当前的线程任务而不做任何处理。
public static class DiscardPolicy implements RejectedExecutionHandler {
        public DiscardPolicy() { }
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
  • 自定义拒绝策略
    扩展RejectedExecutionHandler接口,并捕获异常。
    自定义拒绝策略:抛弃队列中最老的N个任务
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

public class DiscardNOldestPolicy implements RejectedExecutionHandler {
    private int discardNumber;
    public DiscardNOldestPolicy(int discardNumber){
        this.discardNumber = discardNumber;
    }
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        if (executor.getQueue().size() > discardNumber){
           if(!executor.isShutdown()){
               for(int i = 0;i < discardNumber;i++){
                   executor.getQueue().poll();
                   executor.execute(r);
               }
           }
        }
    }
}

线程池工作流程

  1. 如果正在运行的线程数<核心线程数,线程池立刻创建线程并执行该任务;否则放入阻塞队列。
  2. 如果阻塞队列已满,且正在运行的线程数<最大线程数,线程池会创建非核心线程立刻执行该任务。
  3. 如果阻塞队列已满,且正在运行线程数==最大线程数,线程池使用拒绝策略。
  4. 线程任务执行完毕后,从线程池队列中移除,线程池将从队列中取出下一个线程任务继续执行。
  5. 除了核心线程外的线程处于空闲状态超过keepAliveTime,这些空闲线程会被清除。

5种常用的线程池

  • newCachedThreadPool
    无核心线程,队列为SynchronousQueue相当于无缓存队列。一有任务立即执行,有空空闲线程则使用空闲线程处理,否则新建线程来处理。
    适用于任务量大但耗时少的场景
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  • newFixedThreadPool
    全是核心线程,任务大小无限制。
    适用于任务量固定但耗时长的任务。
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }
 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
  • newScheduledThreadPool
    核心线程固定,其他无限制
    适用于定时任务和固定周期的重复任务
public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) {
        if (executor == null)
            throw new NullPointerException();
        return new DelegatedScheduledExecutorService(executor);
    }
static class DelegatedScheduledExecutorService
            extends DelegatedExecutorService
            implements ScheduledExecutorService {
        private final ScheduledExecutorService e;
        DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
            super(executor);
            e = executor;
        }
        public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
            return e.schedule(command, delay, unit);
        }
        //延迟执行
        public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
            return e.schedule(callable, delay, unit);
        }
        //延迟initialDelay执行,并以固定周期period重复执行
        public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
            return e.scheduleAtFixedRate(command, initialDelay, period, unit);
        }
        public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
            return e.scheduleWithFixedDelay(command, initialDelay, delay, unit);
        }
    }
import java.util.concurrent.*;

public class Main{
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
        scheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 3 seconds exec.");
            }
        },3,TimeUnit.SECONDS);
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 1 seconds and repeat exec every 3 seconds.");
            }
        },1,3,TimeUnit.SECONDS);
    }
}

运行结果
在这里插入图片描述

  • newSingleThreadExecutor
    只有一个核心线程
    适用于多任务顺序执行的场景
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  • newWorkStealingPool
    JDK根据当前线程的运行需求向操作系统申请足够的线程,以保障线程的快速执行,并很大程度地使用系统资源,提高并发计算的效率,省去用户根据CPU资源估算并行度的过程。
public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

线程池的任务提交

  • execute
    无返回值,无法判断任务是否被线程池执行成功
    源码:
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

使用

import java.util.concurrent.*;

public class Main{
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10,
                10, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
        for (int i = 0; i < 15; i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("线程:" + Thread.currentThread().getName());
                }
            });
        }
        executor.shutdown();
    }
}

运行结果
在这里插入图片描述

  • submit
    使用submit方法提交任务,会返回一个Future接口对象,可以通过该对象来判断任务是否执行成功。
public Future<?> submit(Runnable task) {
            return e.submit(task);
        }
public <T> Future<T> submit(Callable<T> task) {
            return e.submit(task);
        }
public <T> Future<T> submit(Runnable task, T result) {
            return e.submit(task, result);
        }
        

使用

import java.util.concurrent.*;

public class Main{
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10,
                10, TimeUnit.SECONDS, new ArrayBlockingQueue(10));
        for (int i = 0;i < 15;i++){
            final int result = i;
            Future<Integer> future = executor.submit(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    System.out.println("线程:"+Thread.currentThread().getName());
                    return result;
                }
            });
            Integer integer = null;
            try {
                integer = future.get();
                System.out.println(integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

        }
        executor.shutdown();
    }
}

运行结果
在这里插入图片描述
线程池的关闭

  • shutdown()
    将线程池状态设置为SHUTDOWN状态,然后中断所有不在执行任务的线程
  • shutdownNow()
    将线程池的状态设置成STOP状态,然后中断所有任务相乘,并返回执行任务列表

参考资料

  1. 《offer来了(原理篇)》
  2. https://blog.csdn.net/hzw2017/article/details/80672489
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值