转载自http://www.linuxidc.com/Linux/2014-11/108791.htm

相关类Executor,Executors,AbstractExecutorService,ExecutorService

Executor:整个线程池执行者框架的顶层接口。定义了一个execute方法,整个线程执行者框架的核心方法。

public interface Executor {

void execute(Runnable command);
}

ExecutorService:这是一个接口它继承自Executor,定义了shutdown,shutdownNow,awaitTermination,submit,invokeAll等方法。

AbstractExecutorService:实现了ExecutorService接口中的submit,invokeAll的方法。

public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

在这里,所有submit方法提交的任务最终还是调用了execute方法,execute是接口Executor中定义的方法,AbstractExecutorService没有实现它,

需要子类去实现这个方法,ThreadPoolExecutor继承了AbstractExecutorService,它实现了execute方法。ScheduledThreadPoolExecutor继承自

ThreadPoolExecutor,并覆盖了ThreadPoolExecutor的execute方法。这个方法是线程执行框者架的核心逻辑,不同的线程池执行者有不同的实现逻辑。

AbstractExecutorService的功能较为简单,实现了不同参数的submit,invokeAll方法。

ThreadPoolExecutor线程池执行者:它有一个核心的成员变量:

private final HashSet<Worker> workers = new HashSet<Worker>();

workers可以看做是ThreadPoolExecutor中用于运行任务的线程池。

worker是一个封装了一个Thread对象并实现了Runnable接口的类。封装Thread很容易理解,因为它要利用Thread去运行execute方法提交过来的runnable任务,但是为什么会继承runnable接口呢?

下面是剔除了部分代码的Worker源码:

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
      final Thread thread;
        
        Runnable firstTask;

Worker(Runnable firstTask) {
            setState(-1); 
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

public void run() {
            runWorker(this);
        }

}

Worker是ThreadPoolExecutor的一个内部类,Worker本身实现了Runnable接口,并封装了一个Thread对象,最后在构造方法中获取了一个Runnable对象,这个对象就是ThreadPoolExecutor通过execute提交过来的目标任务。

跟踪runWorker(this)方法:

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); 
        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();//在这里直接调用了目标任务的run方法,并没有将它传给Thread对象。
                    } 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);
        }
    }

回过头来在看看Worker的构造方法:

Worker(Runnable firstTask) {
        setState(-1); 
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

它将自己传给了自己的成员变量thread。目标任务被执行的步骤可能就是:Worker的成员变量thread启动后调用worker的run方法,worker的run方法中将自己传给runWorker,runWorker在调用目标执行对象的run方法。

那么thread是何时被执行的呢?

下面看看ThreadPoolExecutor中的一个其他方法:

private boolean addWorker(Runnable firstTask, boolean core) {
      ......
        try {
            final ReentrantLock mainLock = this.mainLock;
            w = new Worker(firstTask);
            final Thread t = w.thread;//这里初始化一个Worker对象w,在将w的成员变量thread付给t
            if (t != null) {
                mainLock.lock();
                try {
                    int c = ctl.get();
                    int rs = runStateOf(c);

if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();//在这里调用t的start方法。
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

这里为什么会设计的这么绕,我想主要是Worker不仅封装了一个thread,而且对目标任务进行了封装,在运行封装过后的目标任务前,addWorker可以做一些相关操作。

这里仅仅介绍了ThreadPoolExecutor的线程池,那么这个线程池是如何被维护的,下面介绍几个关键的参数。

private volatile int corePoolSize;
  private volatile int maximumPoolSize;
  private final BlockingQueue<Runnable> workQueue;

这三个是ThreadPoolExecutor的成员变量,其中workQueue跟县城池没有关系。workQueue是一个线程安全的阻塞队列。

corePoolSize是线程池的核心大小,maximumPoolSize是线程池的最大大小。

当提交新任务时,如果ThreadPoolExecutor中有线程在运行,并且线程的数量小于corePoolSize,那么就会有新的线程被创建。如果当前运行的线程数大于corePoolSize,就会放到缓存队列workQueue中。如果缓冲队列也满了,就继续创建线程,直到线程的数量达到maximumPoolSize

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

//判断如果当前运行的线程数小于 corePoolSize,添加新的线程(addWorker会添加一个新的线程,上面有介绍),方法直接返回。
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }

//如果当前的运行的线程数大于或等于corePoolSize则新的任务会放到缓存队列中。
        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);

}

还有些其他的参数:

private volatile ThreadFactory threadFactory //线程的工厂函数。
 private volatile RejectedExecutionHandler handler;//任务拒绝的处理类。
 private volatile long keepAliveTime;//任务等待的是将。

ThreadPoolExecutor有几个构造方法来初始化这些参数。Executors类将这些参数简化了来获得一个ExecutorService的引用。

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

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

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

这四个方法中前两个的核心线程数和最大线程数相同,所有可运行的线程数是固定的,<=nThreads。当任务数大于nThreads时,就是放入缓冲队列中。  后两个方法中,线程数是无边界的,核心线程数是0,最大线程数是整型的最大值,然后如果有线程60秒内没有任务运行的话就销毁。每次有新的任务来,都会创建新的线程或使用以前创建的线程(60秒内没有任务运行的线程)。你可能有疑问,既然核心线程数是0,那么所有的任务不是都放到队里里了吗?那么现在就来看看SynchronousQueue这个队里,可以看看这里的介绍 http://www.linuxidc.com/Linux/2014-11/108792.htm 。

回过头来看看任务提交方法的源码:

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)) {//这里是在往队列里方任务,如果不成功就会添加Worker(封装了线程对象)
            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);
    }

上面链接里提到:offer()往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false。

试想一下,第一次提交任务的时候,核心线程数为0,此时没有线程所以没有线程从workQueue中取东西,所以这里的workQueue.offer(command)会返回false,那么就会通过addWorker(command, false)创建一个新的线程。

[转载] Java线程池框架源码分析的更多相关文章

  1. java线程池ThreadPoolExector源码分析

    java线程池ThreadPoolExector源码分析 今天研究了下ThreadPoolExector源码,大致上总结了以下几点跟大家分享下: 一.ThreadPoolExector几个主要变量 先 ...

  2. 深入浅出Java线程池:源码篇

    前言 在上一篇文章深入浅出Java线程池:理论篇中,已经介绍了什么是线程池以及基本的使用.(本来写作的思路是使用篇,但经网友建议后,感觉改为理论篇会更加合适).本文则深入线程池的源码,主要是介绍Thr ...

  3. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  4. Java 线程池框架核心代码分析

    前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和资源消耗都是很高的.线程池应运而生,成为我们管理线程的利器.Java 通过Executor接口,提供了一种标准的方法将任务的提交过 ...

  5. Java调度线程池ScheduledThreadPoolExecutor源码分析

    最近新接手的项目里大量使用了ScheduledThreadPoolExecutor类去执行一些定时任务,之前一直没有机会研究这个类的源码,这次趁着机会好好研读一下. 该类主要还是基于ThreadPoo ...

  6. Java线程池使用和源码分析

    1.为什么使用线程池 在多线程编程中一项很重要的功能就是执行任务,而执行任务的方式有很多种,为什么一定需要使用线程池呢?下面我们使用Socket编程处理请求的功能,分别对每种执行任务的方式进行分析. ...

  7. Java核心复习——线程池ThreadPoolExecutor源码分析

    一.线程池的介绍 线程池一种性能优化的重要手段.优化点在于创建线程和销毁线程会带来资源和时间上的消耗,而且线程池可以对线程进行管理,则可以减少这种损耗. 使用线程池的好处如下: 降低资源的消耗 提高响 ...

  8. 线程池ThreadPoolExecutor源码分析

    在阿里编程规约中关于线程池强制了两点,如下: [强制]线程资源必须通过线程池提供,不允许在应用中自行显式创建线程.说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源 ...

  9. Python线程池ThreadPoolExecutor源码分析

    在学习concurrent库时遇到了一些问题,后来搞清楚了,这里记录一下 先看个例子: import time from concurrent.futures import ThreadPoolExe ...

随机推荐

  1. Android使用RxJava+Retrofit2+Okhttp+MVP练习的APP

    Android使用RxJava+Retrofit2+Okhttp+MVP练习的APP 项目截图     这是我的目录结构 五步使用RxJava+Retrofit2+Okhttp+RxCache 第一步 ...

  2. Hello World -- 第一篇博客

    今年注定是不寻常的一年,因为技术,接触了许多大牛.通过一篇篇博文,看到了大牛们勤奋好学.孜孜不倦的精神,于是决定也开个博客,向大牛学习. 博客开了,写点什么呢?奈何肚子里墨水不多,吐出来也多是白沫,不 ...

  3. springboot自定义配置源

    概述 我们知道,在Spring boot中可以通过xml或者@ImportResource 来引入自己的配置文件,但是这里有个限制,必须是本地,而且格式只能是 properties(或者 yaml). ...

  4. 阿里巴巴Java规约插件试用

    阿里Java开发规约Eclipse插件介绍 阿里巴巴集团配合<阿里巴巴Java开发手册>PDF终极版开发的IDE插件,目前包括IDEA插件.Eclipse插件. 安装 检查环境 插件要求: ...

  5. ubuntu 下修改文件访问权限chmod 777 -R *血的教训!没事别乱开权限!用谁开谁的就行。。。最后不要用这个命令,文件操作全部改用终端

    本文转自: 个人建议 Ubuntu下修改目录权限命令如下:chmod 600 name (只有所有者有读和写的权限)chmod 644 name (所有者有读和写的权限,组用户只有读的权限)chmod ...

  6. datagrid 列鼠标悬浮显示全部信息

    首次发表随笔,且是java新手,求不黑,可能在高手眼里好笑,嘿嘿1,样式设置超过字数限制显示省略号:<style type="text/css"> .datagrid- ...

  7. Linux下文件打包与解压缩

    Linux上存在的文件后缀 文件后缀名 说明 *.zip zip程序打包压缩的文件 *.rar rar程序压缩的文件 *.7z 7zip程序压缩的文件 *.tar tar程序打包,未压缩的文件 *.g ...

  8. 我的第一个python web开发框架(10)——工具函数包说明(一)

    PS:原先是想直接进入功能开发,要用到什么函数时再创建,这样也容易熟悉每个函数的由来和使用方法,但考虑到这样操作,到时会经常在不同文件间切换,不好描述,容易造成混乱,所以还是使用函数库这种方式来说明. ...

  9. Linux入门(12)——解决双系统下Ubuntu16.04不能访问Windows分区

    解决办法一: 进入windows系统,关闭快速启动,关机. 解决办法二: 如果办法一不能解决问题,用这个办法. 查看盘符: sudo fdisk -l 需要在查看盘符后记下目标盘符的数字,比如想解除C ...

  10. Myeclipse10连接SqlServer2012

    本文纯手打!!!! 对于我们这些刚学java的小白,可是真费劲啊!!!! 要用java连接数据库  首先是要通过JDBC驱动 要先去下载一个sqljdbc4.jar,我这里放百度云盘了, 下载地址:链 ...