`

Java线程池核心原理剖析

阅读更多

在系统开发时,我们经常会遇到“池”的概念。使用池一种以空间换时间的做法,通常在内存中事先保存一系列整装待命的对象,以供后期供其他对象随时调用。常见的池有:数据库连接池,socket连接池,线程池等。今天我们就来看一下线程池的概念。


Executor

JDK为我们提供了一套Executor框架来方便我们来管理和使用线程池。
打开java.util.concurrent.Executors类,我们可以发现JDK为我们提供了那么多的方法来帮助我们高效快捷的创建线程池:

1
2
3
4
5
6
public static ExecutorService newFixedThreadPool(int nThreads);//创建一个固定数目的、可重用的线程池
public static ExecutorService newSingleThreadExecutor();//创建一个单线程化的线程
public static ExecutorService newCachedThreadPool();//创建一个可缓存线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);//创建一个支持定时及周期性任务执行的线程池
public static ScheduledExecutorService newSingleThreadScheduledExecutor() ;//创建一个支持定时及周期性任务执行的线程池
public static ExecutorService newWorkStealingPool() ;//创建一个拥有多个任务队列的线程池

上方简单列举了几个Executor框架为我们提供的创建线程池的方法,这些线程池拥有各种各样的功能,我想当你刚刚开始使用线程的时候google如何使用线程池的时候大部分文章都是教你如何使用上方的一些方法创建一个线程池。但是如果你去查看他们的源码就会发现他们最后构造的时候都调用了同一个构造方法。(除了newWorkStealingPool之外,这个我们在下篇文章再讨论)

1
2
3
4
5
6
7
ThreadPoolExecutor(int corePoolSize,//线程池线程数量
                           int maximumPoolSize,//线程中最大的线程数量
                           long keepAliveTime,//线程池线程数量超过corePoolSize的空闲线程的存活时间
                           TimeUnit unit,//keepAliveTime时间单位
                           BlockingQueue<Runnable> workQueue,//被提交还没执行的任务存放在这里
                           ThreadFactory threadFactory,//线程工厂
                           RejectedExecutionHandler handler)//任务过多时的拒绝策略

上方的4个参数我想你看到了就会明白了,现在我们着重来讲一下下面的三个参数。


WorkQueue

参数workQueue是用来存放已提交但还未执行的任务,JDK为我们提供了一下实现:

直接提交队列SynchronousQueue

1
2
3
4
5
6
7
8
9
10
当新任务过来的时候它是这样处理的:
if(有空闲线程){
    处理
}else{
    if(当前线程数<maximumPoolSize){
        创建新线程处理
    }else{
        执行拒绝策略
    }
}

因此使用这个队列时一定要设置很大的maximumPoolSize

有界的任务队列ArrayBlockingQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
if(当前线程数<corePoolSize){
    创建新线程执行
}else{
    if(任务队列是否已满){
       if(当前线程<maximumPoolSize){
          创建新线程处理
       }else{
          执行拒绝策略
        }
    }else{
       放到任务队列
    }
}

无界的任务队列LinkedBlockingDeque

1
2
3
4
5
6
7
8
9
10
11
12
13
if(当前线程数<corePoolSize){
    创建新线程执行
}else{
    放入任务队列,等待执行,直到系统资源耗尽
}

优先任务队列PriorityBlockingQueue
根据任务的优先级将任务存放在任务队列特定位置
if(当前线程数<corePoolSize){
    创建新线程执行
}else{
    等待执行,直到系统资源耗尽
}


线程工厂

第六个参数threadFactory是为线程池中创建线程的,我们使用Executor框架创建的线程就是有threadFactory提供的。我们看一下JDK提供的默认的threadFactory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

重点关注一下其中的newThread方法,看到这个我想你就明白了为什么你使用线程池创建出来的线程打印的时候名字的来源,还有是否是守护线程和优先级等属性的来源了。


拒绝策略

看到刚刚的几种任务队列我们发现当任务过多时是需要指定拒绝策略来进行拒绝呢,那么JDK又为我们提供了哪些拒绝策略呢。

1
2
3
4
AbortPolicy直接抛出异常。
CallerRunsPolicy:如果线程池未关闭,则在调用者线程中运行当前任务
DiscardOldestPolicy:丢弃即将执行的任务,然后再尝试提交当前任务
DiscardPolicy:丢弃此任务


线程池的扩展

ThreadPoolExecutor不仅仅能够创建各种各样的线程来帮助我们实行功能,它还预留了三个接口来供我们进行扩展。

在runWorker方法中调用线程进行执行之前调用了beforeExecute方法,执行之后调用了afterExecute()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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();
                   } 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);
       }
   }

这两个方法在ThreadPoolExecutor类中是没有实现的,我们想要监控线程运行前后的数据就可以通过继承ThreadPoolExecutor类来实现这个扩展。
另外还有一个terminated()方法是在整个线程池退出的时候调用的,我们这里一并扩展。

public class ThreadPoolExecutorDemo extends ThreadPoolExecutor {
    //注意这里因为ThreadPoolExecutor没有无参的构造,所以还需要重写一下构造方法。
    //这里限于篇幅就不贴了
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println(Thread.currentThread().getId()+"执行完成");

    }
    @Override
    protected void terminated() {
        System.out.println("线程池退出");
    }
}

//使用这个demo就可以验证我们扩展的结果了。

public class ThreadPoolDemo {
    static class ThreadDemo extends Thread {
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ":Thread ID is:" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutorDemo threadPoolExecutorDemo=  new ThreadPoolExecutorDemo(5,5,0,TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
        ThreadDemo threadDemo = new ThreadDemo();
        for (int i = 0; i < 20; i++) {
            threadPoolExecutorDemo.submit(threadDemo);
        }
        threadPoolExecutorDemo.shutdown();
    }
}

本文所有源码:https://github.com/shiyujun/syj-study-demo



 

  • 大小: 171.6 KB
1
0
分享到:
评论

相关推荐

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

    总结,Java线程池通过`ThreadPoolExecutor`提供了一种高效管理线程的机制,通过配置核心参数和选择合适的线程池模型,可以优化系统的资源利用和任务调度,提高系统性能。理解线程池的工作原理和核心代码对于编写高效...

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

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

    聊聊并发(3)Java线程池的分析和使用Java开发Jav

    在Java编程中,线程池是一种管理线程资源的有效方式,它可以提高...在阅读《聊聊并发(3)Java线程池的分析和使用》这份文档时,你可以学习到更多关于线程池的实践技巧和案例分析,这对于提升Java开发能力大有裨益。

    详解Java线程池和Executor原理的分析

    Java线程池和Executor原理分析 Java线程池和Executor原理是Java并发编程中非常重要的一部分。线程池是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节。Java...

    Java线程池介绍Java开发Java经验技巧共8页.pd

    本文将深入探讨Java线程池的概念、工作原理以及如何在实际开发中运用。 线程池是由一系列预先创建的线程组成的集合,通过复用这些线程来处理任务,而不是为每个任务创建新的线程。这种设计模式可以减少创建和销毁...

    线程池java

    ### 深入理解Java之线程池 #### 一、Java中的`ThreadPoolExecutor`类 在Java中,`ThreadPoolExecutor`是线程池的核心实现类之一,它提供了丰富的配置选项来...通过以上介绍,相信您对Java线程池有了更深入的理解。

    Java 线程池原理深入分析

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

    线程池核心组件源码剖析.docx

    线程池是Java并发编程中的重要概念,它通过管理和复用线程来提高程序执行效率。在Java中,线程池的核心...阅读和分析源码可以帮助我们更深入地理解这些组件的工作原理,从而更好地利用线程池来提高程序的并发处理能力。

    线程池ThreadPoolExecutor原理源码分析.md

    ### 线程池 `ThreadPoolExecutor` 原理源码分析 #### 一、概述 线程池作为 Java 并发编程中的重要组件,在实际应用中被广泛使用。其核心类 `ThreadPoolExecutor` 实现了对线程的管理、调度等功能。本文将围绕 `...

    Java实现的线程池、消息队列功能

    `CodeReader`类可能实现了读取Java源码中的线程池配置,如线程池的核心线程数、最大线程数等,并解析出线程池的实现细节。同时,它也可能分析了消息队列的使用,如查找消息的发布和订阅代码,识别出消息的类型和格式...

    Java/Android线程池演示Demo

    总结,这个"Java/Android线程池演示Demo"旨在通过实例展示如何在Android和Java项目中使用线程池进行并发处理,帮助开发者理解线程池的工作原理和优势,以及如何根据应用需求配置和管理线程池。通过分析和实践这个...

    Java线程池,正式上线运行的源码,分享欢迎使用并提意见

    Java线程池是一种高效管理并发任务执行的工具,它通过维护一组可重用的线程,减少了创建和销毁线程的开销。在Java中,`java.util.concurrent`包提供了线程池的相关实现,最核心的类是`ExecutorService`、`...

    3.1.8.线程池的实现原理分析1

    下面我们将详细探讨线程池的实现原理、优势以及Java提供的线程池API。 线程池的基本概念是预先创建一定数量的线程,并将它们存储在一个容器(通常是一个队列)中。当需要执行新任务时,线程池会从容器中取出一个...

    100行Java代码构建一个线程池

    【Java线程池实现】 Java通过`java.util.concurrent`包提供了线程池的实现,如`ExecutorService`接口和`ThreadPoolExecutor`类。`ThreadPoolExecutor`允许开发者自定义线程池的核心参数,如核心线程数、最大线程数...

    java 手术任务(线程池)

    通过以上讨论,我们可以看到Java线程池在手术任务中的重要性,它不仅能够提升系统性能,还能帮助开发者更好地管理和控制并发执行的任务。结合`SurgeryThreadPool.java`源码分析和`Java.jpg`中的示例,我们可以进一步...

    两种线程池的实现和性能评价

    通过对HH线程池和LF线程池的实现原理及性能特点进行深入分析,并结合实验数据,可以得出以下结论: - 在处理大量短时间任务时,HH线程池能够通过异步机制提高系统性能; - 对于长时间运行的任务或高并发场景,LF...

    java内置线程池 !!!

    #### 二、线程池工作原理 线程池的主要组成部分包括: 1. **核心线程数**:初始化时创建的线程数量。 2. **任务队列**:用于存放待执行任务的队列。 3. **最大线程数**:线程池允许的最大线程数量。 4. **拒绝策略...

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

    理解Java线程池的原理和配置对于优化并发程序的性能至关重要。通过合理设置线程池参数,可以有效控制线程数量,避免资源浪费,同时确保系统的稳定性和响应性。线程池不仅简化了并发编程,还能通过复用线程、控制并发...

    Core Java.JAVA核心技术(中文版)

    《Core Java.JAVA核心技术(中文版)》是学习Java编程的重要参考资料,主要涵盖了Java语言的基础以及进阶知识。这本书深入浅出地讲解了Java的核心概念和技术,为读者提供了全面而细致的学习路径。以下是对该书内容的...

    java 线程池源码处理并发

    本文将深入解析Java线程池的源码,探讨其工作原理、核心组件以及如何优化并发性能。 Java线程池的实现主要基于`java.util.concurrent`包中的`ExecutorService`接口,而`ThreadPoolExecutor`类是其具体实现。`...

Global site tag (gtag.js) - Google Analytics