java线程池详细入门教程即源码解析

##1、线程池概念
     线程池是线程的集合,通过线程池我们不需要自己创建线程,将任务提交给线程池即可。为什么要使用线程池,首先,使用线程池可以重复利用已有的线程继续执行任务,避免线程在创建和销毁时造成的消耗。其次,由于没有线程创建和销毁时的消耗,可以提高系统响应速度。最后,通过线程可以对线程进行合理的管理,根据系统的承受能力调整可运行线程数量的大小等。

##2、线程池使用示例
     这里做两个测试,首先写一个实现Runnable接口的类:

class MyTask implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName());
	}
	
}

main中代码:

		ExecutorService pool = Executors.newFixedThreadPool(5);
		Runnable myTask = new MyTask();
		Runnable myTask1 = new MyTask();
		Runnable myTask2 = new MyTask();
		Runnable myTask3 = new MyTask();
		Runnable myTask4 = new MyTask();
		pool.execute(myTask);
		pool.execute(myTask1);
		pool.execute(myTask2);
		pool.execute(myTask3);
		pool.execute(myTask4);

这里newFixedThreadPool中的5代表可运行线程数量。所以结果:

pool-1-thread-3
pool-1-thread-1
pool-1-thread-4
pool-1-thread-5
pool-1-thread-2

可以看到这里又五个线程在运行。这里如果将可运行线程数量变成2,那么结果如下:

pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2

可以看到线程1和线程2都被重复利用了,没有创建更多的线程出来。

##3、线程池的状态
     线程池的状态及状态间的切换如下图所示:
这里写图片描述
可以看到线程池有五种状态RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED,不同于线程的转台切换的是,线程池的状态可操作的其实只有两种状态,RUNNING和TERMINATED,线程池一旦创建就会处于RUNNING状态,而TERMINATED是终止,线程池调用shutdown或者shutdownNow线程池其实就会慢慢的切换到终止状态,中间三种状态只是线程由运行态过渡到终止态的中间状态,用来进行一些处理事项的。RUNNING状态是正常运行状态,能够接受新的任务和处理任务,SHUTDOWN是关闭状态,不能接受新的任务,将正在执行的任务处理完毕后进入到TIDYING整理状态,STOP是停止状态,不接受任务,也不处理任务,直接中断任务,然后进入到TIDYING整理状态,TIDYING状态会自动调用terminate方法,调用完后进入到终止状态。

##4、通过源码理解线程池
     首先是线程的创建,线程的创建一般通过一个工厂类创建,

ExecutorService pool = Executors.newFixedThreadPool(2);

进去看这个函数:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

可以看到还是直接new了一个线程池出来,再看这个线程池的构造方法:

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

首先对这几个参数进行说明:
corePoolSize:核心池大小,表示线程池正常情况下能运行的线程最大多少。
maximumPoolSize:最大池大小,表示线程池最大能运行的线程数。
keepAliveTime:表示线程执行任务后的空闲存活时间,过了这个时间线程将被销毁,这个参数的存在也直接实现了概述中所说的前两点优点,线程的消耗可以分为A,B,C三个阶段,A代表创建,B代表执行,C代表消耗,如果有很多个任务需要执行时,B执行所带来的消耗代价小于A,C所带来的消耗代价,那么就通过这个参数,让线程保持一段存活时间,方便执行后面的任务。
unit:线程空闲存活时间的单位。
workQueue:这个是任务队列,将后面的任务加入到这个队列中。
threadFactory:真正用于创建线程的参数。
handler:表示已经不能在接收任务时,调用的处理类。
在这个构造方法中分别对这几个参数进行了初始化。**这里再介绍下这几个参数的关系,当用户创建任务时,如果当前的运行的线程数小于核心池数量,那么会创建新的线程来执行这个任务,如果当前运行的线程数大于核心池的数量,那么就将任务添加到任务队列中,如果任务队列已经装满且当前运行线程数小于最大池的数量,那么就再创建线程来执行这个任务,如果已经等于了最大池的数量,那么将拒绝这个任务并且执行handle处理类。**线程的创建介绍了再来看任务的执行方法execute:

    public void execute(Runnable command) {
	    //当任务为空,抛出空指针异常
        if (command == null)
            throw new NullPointerException();
        //这个是获取到ctl参数的值,这个值是32位的
        //高三位存储了线程池的状态,其余位存储了线程池中运行的线程的数量
        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);
    }

这里多次调用到addWorker方法,这里继续看addWorker方法:

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))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
	//从这里开始重点看
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
          //获取可重入互斥锁
            final ReentrantLock mainLock = this.mainLock;
            //创建一个新的线程来之心这个任务
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
            //线程不为空,加锁
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();
                    int rs = runStateOf(c);

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                            //将worker添加到set集合中
                        workers.add(w);
                        //获取集合的worker数量
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //worker添加成功执行任务
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

到这里线程池的入门介绍结束。

猜你喜欢

转载自blog.csdn.net/liuyuanq123/article/details/80282221