`

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并发编程的艺术》源代码

    第6章介绍了Java中的大部分并发容器,并深入剖析其实现原理,让读者领略大师的设计技巧。 第7章介绍了Java中的原子操作类,并给出一些实例。 第8章介绍了Java中提供的并发工具类,这是并发编程中的瑞士军刀。 第9章...

    Java-并发(Concurrent)编程

    资源概要:1,多线程;2,synchronized;3,volatile;4,多线程在JVM中的实现原理剖析 导语: ...锁Lock-AQS核心原理剖析 并发工具类、并发容器、阻塞队列 线程池原理剖析 线程池案例-Web容器-压力测试

    最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

    │ Java面试题02.java的垮平台原理.mp4 │ Java面试题03.搭建一个java的开发环境.mp4 │ Java面试题04.java中int占几个字节.mp4 │ Java面试题05.java面向对象的特征.mp4 │ Java面试题06.装箱和拆箱.mp4 │ Java...

    《Java并发编程的艺术》

    它选取了Java并发编程中最核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理和方法,对Java并发编程进行了最为深入和透彻的阐述。 《Java并发编程的艺术》内容涵盖...

    JAVA课程学习笔记.doc

    线程池框架2 第一层结构2 接口简介3 核心实现类3 辅助类4 完成服务4 源码原理解析4 线程池执行原理4 调度线程池原理7 异步结果源码分析10

    Java学习指南,涵盖大部分Java程序员所需要掌握的核心知识

    学习路线、Java基础核心串讲、Java基础知识 计算机操作系统 IO Linux 计算机网络 HTTP/HTTPS TCP/IP 7种常见的设计模式和使用场景 Java必会基础与新版本特性 集合框架 JVM内存模型 性能调优、线上问题排查 类加载...

    【Java面试+Java学习指南】一部分大部分Java招聘所需要掌握的核心知识

    Java基础核心串讲 Java基础知识 计算机操作系统 IO Linux 计算机网络 HTTP/HTTPS TCP/IP 7种常见的设计模式及使用场景 Java必会基础与新版本特性 线上问题排查 集合框架 哈希映射 并发哈希映射 数组列表 ...

    Java并发编程的艺术

    它选取了Java并发编程中最核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理和方法,对Java并发编程进行了最为深入和透彻的阐述。, 《Java并发编程的艺术》内容涵盖...

    java核心知识点整理.pdf

    可达性分析............................................................................................................................................... 26 2.3.2. 2.3.3. 老年代 ........................

    JAVA核心知识点整理(有效)

    可达性分析............................................................................................................................................... 26 2.3.2. 2.3.3. 老年代 ........................

    Java-Interview:此项目为 Java 面试的汇总,多数是一些 Java 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题

    线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时内存划分 类加载机制 OOM 分析 垃圾回收 对象的创建与内存分配 你应该知道的 volatile 关键字 分布式相关 分布式限流 基于 Redis 的分布式锁 分布式缓存...

    免费超全面的Java基础类型,容器,并发,IO流,面向对象,Web编程等代码总结

    IO流核心模块及基本原理 曼编程 线程的创建方式与生命周期 线程核心机制,基础概念扩展 多线程并发访问,同步控制 线程间通信,等待/通知机制 锁锁机制,API详解 Fork/Join 框架机制详解 Executor线程池框架简介 ...

    ArtConcurrentBook.rar

    它选取了Java并发编程中核心的技术进行讲解,从JDK源码、JVM、CPU等多角度全面剖析和讲解了Java并发编程的框架、工具、原理和方法,对Java并发编程进行了为深入和透彻的阐述。 《Java并发编程的艺术》内容涵盖Java...

    Java-Interview:https

    线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时内存划分 类加载机制 OOM 分析 垃圾回收 对象的创建与内存分配 你应该知道的 volatile 关键字 分布式相关 分布式限流 基于 Redis 的分布式锁 分布式缓存...

    java版飞机大战源码-java-club:Java程序员所需要掌握的核心知识都在这里。。。。alittledaily~

    整理收录一份Java核心知识图谱 后端常见问题 收集几个参与社区投稿、开源项目的途径 博文收集(敖丙系列&芋道源码系列) 注 : 没链接的是还没写 Java基础核心串讲 计算机操作系统与Linux 7种常见的设计模式和使用...

    Java多线程与并发-原理

    类锁 对比 对象锁同对象,类锁 对比 对象锁代码实现SyncDemoSyncThread总结synchronized底层实现原理实现synchronized基础对象在内存中的布局对象头结构Monitor锁的竞争、获取与释放字节码层面分析什么是重入-...

    java-interview

    线程池原理 深入理解线程通信 交替打印奇偶数 JVM Java 运行时内存划分 类加载机制 OOM 分析 垃圾回收 对象的创建与内存分配 你应该知道的 volatile 关键字 分布式相关 分布式限流 基于 Redis 的分布式锁 分布式缓存...

    深入理解高并发编程-核心技术原理

    (3) 从源码角度分析创建线程池究竟有哪些方式 2、基础案例篇 (1) 导致并发编程频繁出问题的“幕后黑手” (2)工作了3年的程序员小菜面试高并发岗位被吊打虐哭 (3)如何解决可见性和有序性问题?这次彻底懂了! 3、实战...

Global site tag (gtag.js) - Google Analytics