跟着源码看线程池Executors

线程池Executors

Executors中常见线程池的类型:

newFixedThreadPool(int nThreads) 固定长度线程池,只有一个参数,用来指定核心线程数和最大线程数,核心线程数和最大线程数是相同大小。构造方法

new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());

newWorkStealingPool()这里使用的是新线程池,这里暂不讨论。

new ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);

newSingleThreadExecutor()核心线程数和最大线程数都为1的线程池,是固定长度线程池为1的特殊情况。

new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));

newCachedThreadPool ()非固定长度线程池,核心线程池为0,最大线程数为最大整数的线程池,带有允许存活时间参数。

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());

ScheduledExecutorService ()可定时执行线程池。

new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);

Executors中线程池除newWorkStealingPool外,最终会初始化为ThreadPoolExecutor线程池。ThreadPoolExecutor线程池构造方法如下

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

ThreadPoolExecutor线程池的属性

    //阻塞队列,用于存放线程,线程树大于核心线程数和最大线程数时使用
	private final BlockingQueue<Runnable> workQueue;

    //锁
    private final ReentrantLock mainLock = new ReentrantLock();

    //工作集合
    private final HashSet<Worker> workers = new HashSet<Worker>();

    //加锁条件
    private final Condition termination = mainLock.newCondition();

    //最大线程数量
    private int largestPoolSize;

    //已完成任务数量
    private long completedTaskCount;

    //线程工厂
    private volatile ThreadFactory threadFactory;

    //拒绝策略
    private volatile RejectedExecutionHandler handler;

    //存活时间(非核心线程),核心线程创建过后会被销毁,非核心线程不会进行销毁
    private volatile long keepAliveTime;

    //核心线程是否允许销毁
    private volatile boolean allowCoreThreadTimeOut;

    //核心线程数量
    private volatile int corePoolSize;

    //最大线程数量
    private volatile int maximumPoolSize;

    //默认拒绝策类
    private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();

   //
    private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");

构造方法ThreadPoolExecutor,线程池最终调用方法如下,会将核心线程数、最大线程数、允许非核心(当核心线程允许销毁参数为true时,核心线程也根据这个参数进行销毁)空线程存活时间、单位、阻塞队列、线程工厂和饱和策略进行赋值。

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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

线程启动方法,线程启动时调用的时Worker的start方法,该方法启动的是自身的线程,业务具体要运行的线程会写在runWork方法里面,在其内部调用业务线程的run方法,该方法不会启动线程,这是线程池复用的主要依据。线程池提供了两种启动方法execute和submit,submit方法主要是对task进行封装,生成FutureTask任务后进行调用,会比execute多一个返回值,这里只介绍下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);
}

加入到工作集合,这里使用到了核心线程数、最大线程数等参数,当当前运行的线程小于核心线程时,直接调用runWork方法,创建新的线程进行运行,否则将线程加入阻塞队列,如果队列满了,则比较最大线程数

private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    retry:
    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c);
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;
        for (;;) {
    
    
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))//当前执行线程数不小于最大线程数(核心线程数)根据是否核心														    线程区别
                return false;
            if (compareAndIncrementWorkerCount(c))//将工作线程数增加1
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    
    
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
    
    
            final ReentrantLock mainLock = this.mainLock;//加锁,condition和mainLock为类的共有属性,保证锁条件为同一个
            mainLock.lock();
            try {
    
    
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
    
    
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);//加入到工作集合
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
    
    
                mainLock.unlock();
            }
            if (workerAdded) {
    
    
                t.start();//添加成功后调用线程启动方法
                workerStarted = true;
            }
        }
    } finally {
    
    
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

compareAndIncrementWorkerCount调用的是AtomicInteger.compareAndSet方法。使用CAS方法来保证线程安全,该方法并不能保证数据未被修改过(ABA问题),但是该问题对当前线程没有什么影响。我们只需要保证我们拿到的值不会大于执行线程数即可。具体ABA问题解决方法为替换为AtomicMarkableReference 、AtomicStampedReference 。

Worker类,线程池运行线程时并不是直接针对业务直接起线程,那样就达不到线程复用的目的。启动线程,直接启动的是Worker类,而是通过Worker线程间接的执行业务线程。

private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    
    
    private static final long serialVersionUID = 6138294804551838833L;

    //生命一个线程类
    final Thread thread;
    //生命接口
    Runnable firstTask;
    //完成任务数
    volatile long completedTasks;

    //构造方法
    Worker(Runnable firstTask) {
    
    
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    //run方法,线程池实现
    public void run() {
    
    
        runWorker(this);
    }
.

    protected boolean isHeldExclusively() {
    
    
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
    
    
        if (compareAndSetState(0, 1)) {
    
    
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
    
    
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock()        {
    
     acquire(1); }
    public boolean tryLock()  {
    
     return tryAcquire(1); }
    public void unlock()      {
    
     release(1); }
    public boolean isLocked() {
    
     return isHeldExclusively(); }

    void interruptIfStarted() {
    
    
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
    
    
            try {
    
    
                t.interrupt();
            } catch (SecurityException ignore) {
    
    
            }
        }
    }
}

runwork方法,内部为一个while循环,当线程池中task任务不为空,会一直进行循环调用业务线程的run方法,来执行业务(不直接调用start的方法,巧妙地避免了线程频繁的创建和销毁,从而避免浪费资源,这既是线程池创建的目的)。

final void runWorker(Worker w) {
    
    
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
    
    
        while (task != null || (task = getTask()) != null) {
    
    //判断是否还有未完成任务
            w.lock();
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
                try {
    
    
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
    
    
                    task.run();//这里直接调用的是runnable的run方法,不会直接新建线程,复用当前线程。
                } catch (RuntimeException x) {
    
    
                    thrown = x; throw x;
                } catch (Error x) {
    
    
                    thrown = x; throw x;
                } catch (Throwable x) {
    
    
                    thrown = x; throw new Error(x);
                } finally {
    
    
                    afterExecute(task, thrown);
                }
            } finally {
    
    
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
    
    
        processWorkerExit(w, completedAbruptly);
    }
}

获取任务,这里体现了属性allowCoreThreadTimeOut和keepAliveTime参数的使用,不同的参数会在blockqueue中进行体现。

private Runnable getTask() {
    
    
    boolean timedOut = false; // 超时赋值为否
    for (;;) {
    
    
        int c = ctl.get();
        int rs = runStateOf(c);
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
            decrementWorkerCount();
            return null;
        }
        int wc = workerCountOf(c);
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//判断是否允许线程销毁,线程数大于核心线程数或者																	允许核心线程销毁参数为true
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
    
    
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        try {
    
    
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();//这里为如果设置了允许存活时间,若是阻塞队列为空会等待一段时间
            if (r != null)
                return r;
            timedOut = true;//这里为未通过阻塞队列获取到元素,将超时设置为真
        } catch (InterruptedException retry) {
    
    
            timedOut = false;
        }
    }
}

这里workQueue中的poll方法和take方法需要区分一下,poll会在等待一段时间仍然没有元素后直接退出,而take方法则不会,里面会有一个自旋来进行获取锁,避免了重复获取锁带来的性能问题。具体方法如下:

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    
    
    E x = null;
    int c = -1;
    long nanos = unit.toNanos(timeout);
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
    
    
        while (count.get() == 0) {
    
    //自旋锁获取阻塞队列元素
            if (nanos <= 0)
                return null;
            nanos = notEmpty.awaitNanos(nanos);//这里会释放锁,当时间达到所给参数后会退出
        }
        x = dequeue();//获取元素
        c = count.getAndDecrement();//队列长度减
        if (c > 1)
            notEmpty.signal();//激活阻塞的put线程
    } finally {
    
    
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
}

public E take() throws InterruptedException {
    
    
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
  	takeLock.lockInterruptibly();
  	try {
    
    
        while (count.get() == 0) {
    
    //自旋锁获取阻塞队列元素
            notEmpty.await();//这里直接释放线程,没有时间限制
        }
        x = dequeue();
        c = count.getAndDecrement();
        if (c > 1)
            notEmpty.signal();
    } finally {
    
    
        takeLock.unlock();
    }
    if (c == capacity)
        signalNotFull();
    return x;
} 

饱和策略

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    
    
        //
        public CallerRunsPolicy() {
    
     }

        //既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运			行新的任务。
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
            if (!e.isShutdown()) {
    
    
                r.run();//这里直接时run方法,不是addWorker和start表示为调用线程(一般为主线程)
            }
        }
    }

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

    
    public static class DiscardPolicy implements RejectedExecutionHandler {
    
    
        
        public DiscardPolicy() {
    
     }

        //新提交的任务被抛弃
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
        }
    }

    
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    
    
        
        public DiscardOldestPolicy() {
    
     }

        //丢弃最开始提交的任务,然后尝试提交新的任务
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    
    
            if (!e.isShutdown()) {
    
    
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

ScheduledExecutorService 相对于其它的方法,它不是直接通过ThreadPoolExecutor进行处理,它自己重写了execute其通过调用schedule最终调用delayedExecute方法来延迟执行。ScheduledExecutorService 还提供了scheduleAtFixedRate和scheduleWithFixedDelay固定频率和固定延迟时间执行。Eureka注册中心的心跳就是通过ScheduledExecutorService 的schedule方法来实现的。这里等我再研究一下,有机会的话再介绍,有兴趣的童鞋可以自己研究下。

猜你喜欢

转载自blog.csdn.net/qq_35015148/article/details/108490552