`
dorishy
  • 浏览: 11289 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java线程池原理

阅读更多

        平时使用java写多线程的时候经常使用到jdk的提供的线程池,线程池的概念人人都知道,非常好理解。但是jdk底层到底如何实现线程池的呢?或者说是通过什么方式达到线程池中线程复用的效果的呢?我也是带着这个疑问去翻阅了一下jdk6的源码。

 

一、java线程池的小例子

    

public class MyThread implements Runnable {
    private int i = 0;
    MyThread(int i){
        this.i = i;
    }
    @Override
    public void run() {
        try {
            //线程休眠3秒
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //打印当前线程执行完毕时候的时间,线程id,线程编号
        System.out.println("time:" + new Date() + ", threadId:" + Thread.currentThread().getId() + " ,thread " + i);
    }
}

public class ExecutorTestMain {

    public static void main(String[] args) {

        System.out.println("Main start");
        //创建线程池(线程池固定大小3)
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        //生成10个线程丢进线程池中
        for (int i = 0; i < 10; i++) {
            executorService.execute(new MyThread(i));
        }
        System.out.println("Main wait for MyThread");
    }
}

 输出:

Main start
Main wait for MyThread
time:Mon Sep 01 14:52:44 CST 2014, threadId:10 ,thread 2
time:Mon Sep 01 14:52:44 CST 2014, threadId:9 ,thread 1
time:Mon Sep 01 14:52:44 CST 2014, threadId:8 ,thread 0
time:Mon Sep 01 14:52:47 CST 2014, threadId:8 ,thread 5
time:Mon Sep 01 14:52:47 CST 2014, threadId:9 ,thread 4
time:Mon Sep 01 14:52:47 CST 2014, threadId:10 ,thread 3
time:Mon Sep 01 14:52:50 CST 2014, threadId:8 ,thread 6
time:Mon Sep 01 14:52:50 CST 2014, threadId:9 ,thread 7
time:Mon Sep 01 14:52:50 CST 2014, threadId:10 ,thread 8
time:Mon Sep 01 14:52:53 CST 2014, threadId:8 ,thread 9

    从上面一个小例子中,可以看出10个线程一下子全丢进线程中了,但是每次执行只有3个(从执行完成时间和执行的线程id可以看出),3个执行完毕之后,才重新释放出来继续执行后面的。那么jdk是怎么做到这种线程的复用的呢?

 

二、线程池原理

         

//Executors 使用不同工厂方法构造不同需求的线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
     ExecutorService 继承 Executor,线程池接口,定义了线程池执行的一系列接口。

 

   Executors 线程池的工厂类和工具类,提供了构造线程池的工厂方法以及一些线程池操作的工具方法。

  

   对于不同线程池底层复用线程的原理都是一样的,所以我就以小例子中提到的线程池来看看线程池中复用的原理。

 

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    Executors 工厂类实际上就是返回一个ThreadPoolExecutor类,ThreadPoolExecutor实际上就可以理解为我们概念中的池子的概念了。

 

 

1. ThreadPoolExecutor中包含的属性(主要的属性,其他的参考源码)

 

    //线程池的状态,定义了一些状态常量都是int型,主要是方便比较,后面代码中会看到
    //当前状态
    volatile int runState;
    //接收新的任务,并且处理队列中已有的任务(任务的缓存队列)
    static final int RUNNING    = 0;
    //不接受新任务了,但是会处理队列中已有的任务
    static final int SHUTDOWN   = 1;
    //不接受新任务,不处理队列中已有任务,并且终止正在处理的任务
    static final intb STOP       = 2;
    //同STOP,但是所以线程已经终止
    static final int TERMINATED = 3;

    //线程池的缓存队列,用于缓存等待线程的任务
    private final BlockingQueue<Runnable> workQueue;

    //线程池中的所以工作线程(Worker是ThreadPoolExecutor内部类,实现了Runnable,复用的东东,后面详细介绍)
    private final HashSet<Worker> workers = new HashSet<Worker>();

    //空闲线程等待工作的超时设置
    private volatile long  keepAliveTime;
    private volatile boolean allowCoreThreadTimeOut;


    //一般情况下线程池大小,对比于maximumPoolSize来看
    private volatile int   corePoolSize;
    //线程池最大线程,当线程池处于RUNNING,poolSize大于等于corePoolSize以及小于maximumPoolSize,并且向缓存队列添加失败的情况下,会启用新的Worker来处理任务,如果poolSize超过了maximumPoolSize,那么任务将被拒绝处理
    private volatile int   maximumPoolSize;
    //当前线程数量
    private volatile int   poolSize;
    //任务被拒绝处理的处理类
    private volatile RejectedExecutionHandler handler;
    //线程的工厂类,默认实现是DefaultThreadFactory
    private volatile ThreadFactory threadFactory;

 

2. executorService.execute(new MyThread(i))

 

//executorService.execute(new MyThread(i));
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    //1.若果poolSize<corePoolSize,那么启用新的Worker处理任务(addIfUnderCorePoolSize)
    if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {
        //2.poolSize >= corePoolSize ,那么放入任务缓存队列(workQueue.offer)
        if (runState == RUNNING && workQueue.offer(command)) {
            //3.runState != RUNNING 则处理后续情况
            if (runState != RUNNING || poolSize == 0)
                ensureQueuedTaskHandled(command);
        }
        //4.poolSize >= corePoolSize,放入缓冲失败的情况(workQueue.offer失败)
        else if (!addIfUnderMaximumPoolSize(command))
            reject(command); // is shutdown or saturated
    }
}

 

3 . 创建新的Worker处理任务

        addIfUnderCorePoolSize 和 addIfUnderMaximumPoolSize 处理逻辑一致,下面以addIfUnderCorePoolSize为例说明

private boolean addIfUnderCorePoolSize(Runnable firstTask) {
    Thread t = null;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //addIfUnderMaximumPoolSize这里的if判断条件是poolSize < maximumPoolSize && runState == RUNNING
        if (poolSize < corePoolSize && runState == RUNNING)
            //addTread返回的线程是Worker
            t = addThread(firstTask);
    } finally {
        mainLock.unlock();
    }
    if (t == null)
        return false;
    //执行线程,会执行到Worker的run方法
    t.start();
    return true;
}

 

private Thread addThread(Runnable firstTask) {
    //将任务封装成Worker
    Worker w = new Worker(firstTask);
    //创建Worker线程
    Thread t = threadFactory.newThread(w);
    if (t != null) {
        w.thread = t;
        workers.add(w);
        int nt = ++poolSize;
        if (nt > largestPoolSize)
            largestPoolSize = nt;
    }
    return t;
}

    

4. Worker

        Worker就可以理解为线程池中的工作线程了,所有提交上来的线程都是在在这些工作线上上面执行的,那么具体是如何做到执行和复用的呢?下面我们就一探究竟。

 

        Worker实际上是ThreadPoolExecutor的一个内部类,实现了Runnable接口。主要属性:

//初始化Worker的时候第一个任务
private Runnable firstTask;

//worker执行的任务数
volatile long completedTasks;

//worker所运行的thread对象
Thread thread;

    线程执行的时候会执行到Worker中的run方法

public void run() {
    try {
        Runnable task = firstTask;
        firstTask = null;
        //getTask 就是从缓冲中获取任务,我们可以看到,这个Worker线程会循环执行,不停的从缓冲队列中获取任务执行
        while (task != null || (task = getTask()) != null) {
            //获取任务执行
            runTask(task);
            task = null;
        }
    } finally {
        workerDone(this);
    }
}
Runnable getTask() {
    //从缓存队列中获取任务执行(当然获取的逻辑中涉及到很多当前线程池不同状态的情况)
    for (;;) {
        try {
            int state = runState;
            if (state > SHUTDOWN)
                return null;
            Runnable r;
            if (state == SHUTDOWN)  // Help drain queue
                r = workQueue.poll();
            else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
                r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
            else
                r = workQueue.take();
            if (r != null)
                return r;
            if (workerCanExit()) {
                if (runState >= SHUTDOWN) // Wake up others
                    interruptIdleWorkers();
                return null;
            }
            // Else retry
        } catch (InterruptedException ie) {
            // On interruption, re-check runState
        }
    }
}

         我们都知道,线程执行的过程中都一些自己执行相关的信息,这些信息显然是不可能给其他任务去复用的,所以在执行之前需要去做一些清理工作。

 

private void runTask(Runnable task) {
    final ReentrantLock runLock = this.runLock;
    runLock.lock();
    try {
        /*
         * Ensure that unless pool is stopping, this thread
         * does not have its interrupt set. This requires a
         * double-check of state in case the interrupt was
         * cleared concurrently with a shutdownNow -- if so,
         * the interrupt is re-enabled.
         */
        if (runState < STOP &&
            Thread.interrupted() &&
            runState >= STOP)
            thread.interrupt();
        /*
         * Track execution state to ensure that afterExecute
         * is called only if task completed or threw
         * exception. Otherwise, the caught runtime exception
         * will have been thrown by afterExecute itself, in
         * which case we don't want to call it again.
         */
        boolean ran = false;
        //重新初始化thread中的ThreadLocals等信息
        beforeExecute(thread, task);
        try {
            //任务的执行
            task.run();
            ran = true;
            //后置处理
            afterExecute(task, null);
            ++completedTasks;
        } catch (RuntimeException ex) {
            if (!ran)
                afterExecute(task, ex);
            throw ex;
        }
    } finally {
        runLock.unlock();
    }
}

 

分享到:
评论

相关推荐

    JAVA线程池原理以及几种线程池类型介绍

    ### JAVA线程池原理及几种线程池类型的详细介绍 #### 一、线程池的引入背景及重要性 在现代软件开发中,特别是在基于Java的应用程序设计中,线程池技术已经成为提高系统性能和资源利用率的关键手段之一。线程池...

    Java concurrency线程池之线程池原理(一)_动力节点Java学院整理

    Java concurrency线程池之线程池原理(一)_动力节点Java学院整理,动力节点口口相传的Java黄埔军校

    JAVA线程池原理以及几种线程池类型介绍.doc

    Java线程池是一种高效管理线程的工具,它允许开发者预先创建一组线程,并复用它们来处理任务,从而降低了创建和销毁线程的开销。线程池的使用尤其适用于处理大量短小任务的场景,例如Web服务器、数据库服务器等。在...

    JAVA线程池原理实例详解

    JAVA线程池原理实例详解 JAVA线程池是java中的一种高效的线程管理机制,它可以控制线程的创建、销毁和复用,从而提高系统的性能和可靠性。下面我们将详细介绍JAVA线程池的原理、创建、使用方法及相关注意事项。 一...

    java线程池封装j

    本文将深入探讨Java线程池的原理、封装以及相关知识点。 ### 1. Java线程池概念 Java线程池由`java.util.concurrent`包中的`ExecutorService`接口和其子类实现。其中,最常用的是`ThreadPoolExecutor`类,它提供了...

    Java 线程池的原理与实现

    理解Java线程池的原理和实现,可以帮助我们更有效地管理并发任务,提升系统性能,同时避免资源浪费和线程安全问题。在实际开发中,合理配置线程池参数,结合业务场景选择合适的线程池类型,是优化系统性能的关键步骤...

    JAVA线程池的原理与实现.pdf

    Java线程池是一种高效利用系统资源、管理并发执行任务的机制。...总的来说,理解Java线程池的工作原理和实现对于优化并发应用程序至关重要,它可以帮助我们更好地控制系统的并发度,提高系统的响应速度和资源利用率。

    Java常用线程池原理及使用方法解析

    Java线程池是一种高效管理线程的工具,它允许开发者预先创建一组线程,当需要执行新任务时,线程池会从池中选择一个空闲线程来执行任务,而不是每次都创建新的线程。线程池的使用有助于减少线程创建和销毁的开销,...

    深入探索:Java线程池的工作原理与实践

    本文将深入探讨Java线程池的工作原理,并通过实际代码示例,展示如何高效地使用线程池。 Java线程池是并发编程中的一个重要工具,它通过减少线程创建和销毁的开销,提高了程序的性能和资源利用率。理解线程池的...

    自定义实现Java线程池

    ### 自定义实现Java线程池 #### 一、概述 在深入探讨自定义Java线程池之前,我们先简要回顾一下线程池的基本概念及其重要性。线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动...

    Java 线程池原理深入分析

    总结Java线程池是Java多线程编程的重要工具,通过合理的配置和使用,可以有效地管理和调度线程,提高系统性能。理解线程池的工作原理和参数配置,对于编写高效、稳定的多线程程序至关重要。在实际应用中,应根据任务...

    Java线程池文档

    Java线程池是一种高效管理线程的机制,它允许开发者预先设定线程的数量,并通过池化的方式重用已创建的线程,以提高系统性能,减少线程的创建和销毁开销。线程池在Java中是通过`java.util.concurrent`包下的`...

    java多线程详解,线程池原理,8种锁,java内存模型......

    首先希望大家喜欢我制作的文档,如果文档中有什么误解的地方,望告诉一下,5分是也不多,是系统默认的,那么就5分咯,java多线程详解,线程池原理,8种锁,java内存模型......

    java线程池实例

    Java线程池是一种高效管理线程资源的工具,它通过维护一组可重用的线程来减少创建和销毁线程的开销。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口和它的实现类,如`ThreadPoolExecutor`,来支持...

    深入理解Java线程池(PPT:原理+代码)

    通过剖析Java中线程池的原理,解读Java线程池源码,并给出线程池调用的示例,帮助理解线程池的基本原理。

    java 线程池实现多并发队列后进先出

    Java线程池是一种高效管理并发任务的机制,它允许开发者预先配置一定数量的线程,以便在处理多个并发任务时能有效地复用这些线程,从而避免了频繁创建和销毁线程带来的开销。在Java中,`java.util.concurrent`包下的...

    JAVA线程池例子

    Java线程池是一种高效管理线程资源的技术,它允许开发者创建一组可重用的工作线程,从而避免频繁地创建和销毁线程带来的性能开销。线程池在Java中主要通过`java.util.concurrent`包中的`ExecutorService`接口及其...

    基于Java线程池技术的数据爬虫设计与实现.pdf

    本文所提及的基于Java线程池技术的数据爬虫设计与实现,不仅涉及到了数据爬虫的原理和架构,还包括了多线程编程的知识点,以及线程池技术在数据爬虫中的具体应用。 首先,数据爬虫的基本原理是模拟用户的点击行为,...

    Java线程池及观察者模式解决多线程意外死亡重启问题

    Java线程池是Java并发编程中的重要组成部分,它允许开发者高效地管理多个并发执行的线程,有效地控制系统的资源消耗,提高系统性能和稳定性。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口及其...

    鸟之家破解器源码

    鸟之家破解器源码

Global site tag (gtag.js) - Google Analytics