线程池解析


为什么使用线程池?

使用线程池主要有以下两个原因:
1、提高线程的可控性;
2、降低系统资源消耗;
更详细的可描述为:
a. 高性能:
如果希望高性能的执行任务,即只要执行线程未达到线程池最大容量,则来了新线程之后立即去执行;此种方式可选择SynchronousQueue作为缓存队列,SynchronousQueue在运行offer方法时,如果没有其他线程poll,则返回false,
按照ThreadPoolExecutor的实现,此时就会在最大线程数允许的情况下创建线程;如果其他线程调用poll,则返回true,按照ThreadPoolExecutor的实现,任务不会缓存在队列中,而是直接交由线程执行;这种情况下ThreadPoolExecutor
能支持最大线程数目的线程执行;
b. 缓冲执行
如果希望Runnable的线程尽量在CorePoolSize的范围内执行,则选用ArrayBlockingQueue或者LinkedBlockingQueue作为缓冲队列,当当前执行的线程数达到CorePoolSize的时候,则将线程加入到缓冲队列,而不是
直接交由线程池进行执行,这样最多能支持CorePoolSize + BlockingQueue.size()大小的任务数的执行

常见线程池创建方式?

线程池的创建使用Executors类进行,常用的线程池种类都在Executors类中,具体创建语句为:
ExecutorService exec = Executors.newFixedThreadPool(int nThreads);
线程池大多返回ThreadPoolExecutor,少部分返回ForkJoinPool,根据需要可直接使用ThreadPoolExecutor来创建线程,如下:

// ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
						BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, 
						RejectedExecutionHandler handler)
ThreadPoolExecutor executor = new ThreadPoolExecutor(
											10, 600, 30, TimeUnit.SECONDS, 
											queue, Executors.defaultThreadFactory(), 
											new ThreadPoolExecutor.AbortPolicy());
newFixedThreadPool
创建一个定长的线程池,超过的线程在队列中等待;
public static ExecutorService newFixedThreadPool(int nThreads) {
			/* nThreads: 线程池中的核心线程数;
			   nThreads: 线程池中的最大线程数;
			   0L: 超时时间;
			   TimeUnit.MILLISECONDS: 超时时间单位;
			   LinkedBlockingQueue: 存储线程的队列;
			*/
		return new ThreadPoolExecutor(nThread, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
newWorkStealingPool
根据所需的并发数动态创建和关闭线程,能够合理的使用CPU的资源,所以比较适合使用在耗时时间较长的任务中;返回的类型为 ForkJoinPool
public static ExecutorService newWorkStealingPool(int parallelism) {
       return new ForkJoinPool
           (parallelism,
            ForkJoinPool.defaultForkJoinWorkerThreadFactory,
            null, true);
 }

什么是ForkJoinPool?
public ForkJoinPool(int parallelism,
                       ForkJoinWorkerThreadFactory factory,
                       UncaughtExceptionHandler handler,
                       boolean asyncMode) {
       this(checkParallelism(parallelism),
            checkFactory(factory),
            handler,
            asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
            "ForkJoinPool-" + nextPoolId() + "-worker-");
       checkPermission();
   }
使用一个无限队列来保存线程,可以传入线程的数量,如果不传,则用CPU的数量;使用分治法来解决问题,使用fork()和join()来进行调用;
newSingleThreadExecutor
创建只有一个线程同时进行工作的线程池;
public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService
          (new ThreadPoolExecutor(1, 1,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>()));
}
newCachedThreadPool
创建一个可缓存的线程池,可对线程资源进行回收,若没有可回收使用的线程,则新建线程处理任务;
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                     60L, TimeUnit.SECONDS,
                                     new SynchronousQueue<Runnable>(),
                                     threadFactory);
}
newScheduledThreadPool
创建定时任务线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
       return new ScheduledThreadPoolExecutor(corePoolSize);
}

源码简析

Executor结构如下:
在这里插入图片描述
其中Executor是一个接口,其中只有一个方法:void execute(Runnable command);
ExecutorService也是定义的一个接口,包含的方法如下:

在这里插入图片描述
AbstractExecutorService则是一个抽象类,实现了ExecutorService接口,此处不详解。
在这里插入图片描述
ThreadPoolExecutor是整个线程池中的核心类,继承了AbstractExecutorService类。

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

线程提交的方法:

1.execute(Runnable command)
public void execute(Runnable command) {
    // 如果Comand为空,则抛出NPE
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();   // ct1存储线程状态和线程数量
    // 1、获取c的低29位即活动线程数,并和核心线程数进行比较,小于核心线程数,则将线程加入到执行队列,加入成功则返回,
    // 否则重新获取线程状态和workerCount
    if (workerCountOf(c) < corePoolSize) {  
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 2、判断线程是否运行并成功加入到队列中,如果成功加入队列,进行二次检查(因为存在线程死亡情况),判断能否增加新线程或当加入该线程时线程池是否已经关闭;
    // 所以此处进行二次检查,有需要的话还会进行回滚,再加入线程池中
    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);
    }
    // 3、如果没有将线程加入到队列中,则尝试重新建一个线程加入,如果失败(可能原因:线程池已经关闭),则拒绝该任务。
    else if (!addWorker(command, false))
        reject(command);
}

总结execute()方法,流程图如下:

在这里插入图片描述

在该方法中,使用了一个addWorker(Runnable command, boolean core)方法,该方法用于在线程池中创建一个线程并执行。其中,
Runnable command是指定新增线程执行的第一个任务或不执行任务;
boolean core是选择线程池数的依据,true则表示取核心线程数,false表示取最大线程数。

private boolean addWorker(Runnable firstTask, boolean core) {
	/*
	  1、首先获取任务的状态,若状态值大于shutdown或同时满足任务为空且阻塞队列不为空的条件,则新增线程失败,返回false;
	  否则,若线程数大于选择的线程数,则新增线程失败,返回false;
	  否则,增加WorkerCount(原子操作),成功,则跳出retry循环,执行下一步,否则比较任务状态是否变更,变更则循环retry。
	*/
    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))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

	/*
	  2、新建线程执行worker任务,将worker任务加入到workers集合中(此处使用ReentrantLock锁,因为workers是一个HashSet,线程不安全);
	  比较workers的和largestPoolSize的大小,更新largestPoolSize的大小;
      添加任务成功之后,启动线程开始执行。更新workerStarted的值并返回结果。
	*/
    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;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                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;
}

在该方法中,w = new Worker(firstTask);该语句是新建了一个Worker。
	private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {...}
可以看出,其是一个线程任务类,在addWorker方法中,调用t.start()即是重写了run()方法。

Worker中没有用ReentrantLock锁来实现的原因主要是:tryAcquire()方法是不允许重入的,但是ReentrantLock是可重入锁,然而如果线程正在运行则是不允许其他锁进入的。

t.start()即是重写的run()方法中,具体执行是调用了runWorker(Worker w)方法,该方法是不断获取任务task并执行,只要task不为空或线程池没有被关闭,则一直循环。
【注】:在runWorker方法中,给每一个工作线程都会加锁,这是因为shutdown()方法和getTask()方法存在竞价条件。
2.submit()
submit(Callable<?>Task)该方法是在ExecutorService接口中定义的,在AbstractExecutorService中实现。
方法是有返回的方法,方法参数可为Callable、Runnable等,源码如下:

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);    // 此处的execute()方法即为上述的方法
    return ftask;
}

返回值是通过Future来获取的,submit方法中,最后返回的是一个ftask,如下:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
    return new FutureTask<T>(runnable, value);
}

protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
    return new FutureTask<T>(callable);
}
通过上述两个newTaskFor方法可看出,submit最后返回的其实是FutureTask实例,该实例实现了Runnable和Future接口。
Future是Java线程Future模式实现,可以用来进行异步运算;Runnable接口表示可作为一个线程执行;所以意味着FutureTask代表一步计算的结果,同时可以作为一个线程交给Executor来执行。
四种拒绝策略?
RejectedExecutionHandler的rejectedExecution方法有以下四种:

a. CallerRunsPolicy:当达到最大线程数,则将线程交由调用者线程来执行此Runnable;
b. AbortPolicy: 当线程池中的线程数等于最大线程数,抛出RejectedExecutionException;
c. DiscardPolicy: 采用此种策略,当线程池中的线程数等于最大线程数,不做任何操作;
d. DiscardOldestPolicy:当线程池中的线程数等于最大线程数,抛弃要执行的最后一个Runnable任务,执行最新传入的Runnable任务;

使用如下:
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 600, 30, TimeUnit.SECONDS, queue,
														Executors.defaultThreadFactory(), 
														new ThreadPoolExecutor.AbortPolicy());

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值