文档章节

Java线程池

nullpointerxyz
 nullpointerxyz
发布于 07/04 20:11
字数 1271
阅读 89
收藏 0

「深度学习福利」大神带你进阶工程师,立即查看>>>

前言

Java中线程池属于jdk1.5版本加入的任务执行模块功能,任务执行是一个生产者-消费者模式,任务执行的顶层抽象是Executor接口,ExecutorService接口实现了Executor,线程池的抽象是ThreadPoolExecutor类,继承关系如下图:

Executors是一个工具类,内置了多种创建线程池的方法:

  • newFixedThreadPool:固定长度线程池
  • newCachedThreadPool :可缓存线程池
  • newSingleThreadExecutor:单线程的线程池
  • newScheduledThreadExecutor:固定长度的,可以延迟或定时执行的线程池

其内部实现不过是调用了ThreadPoolExecutor的不同构造方法,所以只要理解了ThreadPoolExecutor最全的构造方法的参数含义,自然就理解了上面列举的不同线程池的作用及区别。另外,在Alibaba编码规范中明确要求,线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样可以最大限度的保持灵活性,增加自己的线程名称,监控等,让其他人更好懂线程池的运行规则。所以我们以下只关注ThreadPoolExecutor类的用法。

ThreadPoolExecutor详解

public ThreadPoolExecutor(int corePoolSize, //线程池基本大小
                          int maximumPoolSize,//线程池最大大小
                          long keepAliveTime,//线程空闲存活时间
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,//阻塞队列
                          ThreadFactory threadFactory,//定制线程工厂
                          RejectedExecutionHandler handler//饱和策略) {}
  • 基本大小是即使没有任务,依然保持这个数量。
  • 最大数量也就是基础线程不够用的时候,创建新线程,最多创建这么多线程。
  • 空闲时间指如果超出基本数量的线程,空闲时间超过了配置时间就可以被回收。
  • 阻塞队列参数BlockingQueue<Runnable> workQueue,这是用来保存等待执行的任务,在构造方法中可以限制数量。
  • 线程工厂用来定制线程对象。
  • 不能无限制的创建线程,这样在糟糕的情况下会耗尽系统资源,通常是设置一个最大大小,那么问题来了,超过之后怎么办?交给饱和策略。

下面这行代码定义了一个默认10,最大100个线程,多余线程执行完成任务立刻回收,多余任务被保存在基于链表的,先进先出的阻塞队列中,最多保存200个任务,线程名称叫做mythread,队列中超过100个任务时打印一句log的线程池。

ThreadPoolExecutor executor=new ThreadPoolExecutor(10, 100, 0L,
        TimeUnit.SECONDS, new LinkedBlockingDeque<>(200), r -> {
            Thread thread = new Thread(r);
            thread.setName("mythread");
            return thread;
        }, (r, executor1) -> System.out.println("超过最大线程处理能力"));

饱和策略(拒绝策略)

饱和策略的抽象是接口RejectedExecutionHandler,只有一个方法void rejectedExecution(Runnable r, ThreadPoolExecutor executor);。我们可以实现自己的饱和策略,默认的4种饱和策略:

  • Abort:默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常。

  • Discard:直接抛弃,任务不执行。

  • DiscardOldest:抛弃最旧的,然后尝试提交新的任务。

  • Caller-Runs:调用者运行,将任务回退到调用者,也就是在主线程中执行。

Caller-Runs源码:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

线程工厂

这里有个ThreadFactory参数,ThreadFactory是干什么的呢?顾名思义,Factory在编程中不就是工厂模式么,通常用来构造一个对象。ThreadFactory接口里面只有一个方法:Thread newThread(Runnable r),目的就是定制化Thread对象。

Executors的默认ThreadFactory源码如下,定制了线程组,线程名称:

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

统计监控

ThreadPoolExecutor还有3个重要的方法,修饰符是protected,子类重写可以实现类似统计监控的功能,顾名思义,方法的含义为:在每个线程执行之前,每个线程执行之后和线程池完成关闭操作时执行。

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }

一个示例

public class MyThreadPoolExecutor extends ThreadPoolExecutor {
    public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("线程" + Thread.currentThread().getId() + "执行结束");
    }

    @Override
    protected void terminated() {
        System.out.println("线程池关闭,释放资源");
    }

    public static void test() {
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(10, 100, 0L,
                TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(100), r -> {
            Thread thread = new Thread(r);
            thread.setName("mythread");
            return thread;
        }, new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i < 200; i++) {
            int num = i;
            myThreadPoolExecutor.execute(() -> {
                try {
                    //模拟耗时任务,不让线程快速执行完,导致任务积压,触发拒绝策略
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("任务" + num + "被执行");
            });
        }
        myThreadPoolExecutor.shutdown();
    }
}
nullpointerxyz
粉丝 6
博文 51
码字总数 49323
作品 0
济南
程序员
私信 提问
加载中
请先登录后再评论。
我的架构演化笔记 功能1: 基本的用户注册

“咚咚”,一阵急促的敲门声, 我从睡梦中惊醒,我靠,这才几点,谁这么早, 开门一看,原来我的小表弟放暑假了,来南京玩,顺便说跟我后面学习一个网站是怎么做出来的。 于是有了下面的一段...

强子哥哥
2014/05/31
976
3
Swift百万线程攻破单例(Singleton)模式

一、不安全的单例实现 在上一篇文章我们给出了单例的设计模式,直接给出了线程安全的实现方法。单例的实现有多种方法,如下面: class SwiftSingleton { } 这段代码的实现,在shared中进行条...

一叶博客
2014/06/20
3.5K
16
研究虚拟机--Jikes RVM

Jikes研究虚拟机(Jikes Research Virtual Machine,简称Jikes RVM)是一种成熟的用于执行Java程序的虚拟机,其早期版本与当前版本分别在通用公共许可证(CPL)与Eclipse公共许可证(EPL)下开...

匿名
2013/02/13
1.1K
0
Swing界面分析和调试工具--Swing Inspector

Swing Inspector是一个Java Swing/AWT用户界面分析和调试工具,功能与firebug类似,具有强大的Swing/AWT用户界面分析和调试相关功能。 适用于从java swing初级到高级的所有开发人员,能够快速...

匿名
2013/03/06
3.4K
0
Java™ 编译器--Janino

Janino是一个超级小但又超级快的Java™ 编译器. 它不仅能像javac工具那样讲一组源文件编译成字节码文件,还可以对一些Java表达式,代码块,类中的文本(class body)或者内存中源文件进行编译,...

匿名
2013/04/02
4.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Subversion存储库中“分支”,“标记”和“主干”的含义是什么?

问题: I've seen these words a lot around Subversion (and I guess general repository) discussions. 我已经在Subversion(我猜通用存储库)讨论中看到了很多这样的话。 I have been us......

富含淀粉
今天
5
0
《Java8实战》笔记(03):Lambda表达式

本文源码 Lambda 管中窥豹 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。 Lambda表达...

巨輪
今天
7
0
从其他文件夹导入文件 - Importing files from different folder

问题: I have the following folder structure. 我有以下文件夹结构。 application/app/folder/file.py and I want to import some functions from file.py in another Python file which r......

javail
今天
22
0
大数据研发学习之路--Hadoop集群搭建

阅读编译文档 准备一个hadoop源码包,我选择的hadoop版本是:hadoop-2.7.7-src.tar.gz,在hadoop-2.7.7的源码 包的根目录下有一个文档叫做BUILDING.txt,这其中说明了编译hadoop所需要的一些...

DSJ-shitou
今天
8
0
OSChina 周五乱弹 —— 特么是别的公司派来的特洛伊木马吧?

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 小小编辑推荐:《我会守在这里》- 毛不易 《我会守在这里》- 毛不易 手机党少年们想听歌,请使劲儿戳(这里) @FalconChen :股市连跪了五天,...

小小编辑
今天
77
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部