java预定义的哪四种线程池?

  • newSingleThreadExexcutor:单线程数的线程池(核心线程数=最大线程数=1)
  • newFixedThreadPool:固定线程数的线程池(核心线程数=最大线程数=自定义)
  • newCacheThreadPool:可缓存的线程池(核心线程数=0,最大线程数=Integer.MAX_VALUE)
  • newScheduledThreadPool:支持定时或周期任务的线程池(核心线程数=自定义,最大线程数=Integer.MAX_VALUE)

四种线程池有什么区别?

上面四种线程池类都继承ThreadPoolExecutor,在创建时都是直接返回new ThreadPoolExecutor(参数),它们的区别是定义的ThreadPoolExecutor(参数)中参数不同,而ThreadPoolExecutor又继承ExecutorService接口类

  • newFixedThreadPool
定义:
ExecutorService executorService=Executors.newFixedThreadPool(2);

缺点:使用了LinkBlockQueue的链表型阻塞队列,当任务的堆积速度大于处理速度时,容易堆积任务而导致OOM内存溢出

  • newSingleThreadExecutor
定义:ExecutorService executorService =Executors.newSingleThreadExecutor();

上面代码神似new FixedThreadPoop(1),但又有区别,因为外面多了一层FinalizableDelegatedExecutorService,其作用:

可知,fixedExecutorService的本质是ThreadPoolExecutor,所以fixedExecutorService可以强转成ThreadPoolExecutor,但singleExecutorService与ThreadPoolExecutor无任何关系,所以强转失败,故newSingleThreadExecutor()被创建后,无法再修改其线程池参数,真正地做到single单个线程。

缺点:使用了LinkBlockQueue的链表型阻塞队列,当任务的堆积速度大于处理速度时,容易堆积任务而导致OOM内存溢出

  • newCacheThreadPool
定义:ExecutorService executorService=Executors.newCacheThreadPool();

缺点:SynchronousQueue是BlockingQueue的一种实现,它也是一个队列,因为最大线程数为Integer.MAX_VALUE,所有当线程过多时容易OOM内存溢出

  • ScheduledThreadPool
定义:ExecutorService executorService=Executors.newScheduledThreadPool(2);
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
//ScheduledThreadPoolExecutor继承ThreadPoolExecutor
return new ScheduledThreadPoolExecutor(corePoolSize);
} public ScheduledThreadPoolExecutor(int corePoolSize) {
//ScheduledThreadPoolExecutor继承ThreadPoolExecutor,故super()会调用ThreadPoolExecutor的构造函数初始化并返回一个ThreadPoolExecutor,而ThreadPoolExecutor使实现ExecutorService接口的
//最终ScheduledThreadPoolExecutor也和上面几种线程池一样返回的是ExecutorService接口的实现类ThreadPoolExecutor
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}

线程池有哪几个重要参数?

ThreadPoolExecutor构造方法如下:

  • keepAliveTime是指当前线程数位于 [核心线程数,最大线程数] 之间的这些非核心线程等待多久空闲时间而没有活干时,就退出线程池;
  • 等待丢列的大小与最大线程数是没有任何关系的,线程创建优先级=核心线程 > 阻塞队列 > 扩容的线程(当前核心线程数小于最大线程数时才能扩容线程)
  • 假如核心线程数5,等待队列长度为3,最大线程数10:当线程数不断在增加时,先创建5个核心线程,核心线程数满了再把线程丢进等待丢列,等待队列满了(3个线程),此时会比较最大线程数(只有等待丢列满了最大线程数才能出场),还可以继续创建2个线程(5+3+2),若线程数超过了最大线程数,则执行拒绝策略;
  • 假如核心线程数5,等待队列长度为3,最大线程数7:当线程数不断在增加时,先创建5个核心线程,核心线程数满了再把线程丢进等待丢列,当等待队列中有2个线程时达到了最大线程数(5+2=7),但是等待丢列还没满所以不用管最大线程数,直到等待丢列满了(3个阻塞线程),此时会比较最大线程数(只有等待丢列满了最大线程数才能出场),此时核心+等待丢列=5+3=8>7=最大线程数,即已经达到最大线程数了,则执行拒绝策略;
  • 如果把等待丢列设置为LinkedBlockingQueue无界丢列,这个丢列是无限大的,就永远不会走到判断最大线程数那一步了

如何自定义线程池

可以使用有界队列,自定义线程创建工厂ThreadFactory和拒绝策略handler来自定义线程池

public class ThreadTest {
public static void main(String[] args) throws InterruptedException, IOException {
int corePoolSize = 2;
int maximumPoolSize = 4;
long keepAliveTime = 10;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(2);
ThreadFactory threadFactory = new NameTreadFactory();
RejectedExecutionHandler handler = new MyIgnorePolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
workQueue, threadFactory, handler);
executor.prestartAllCoreThreads(); // 预启动所有核心线程
for (int i = 1; i <= 10; i++) {
MyTask task = new MyTask(String.valueOf(i));
executor.execute(task);
}
System.in.read(); //阻塞主线程
}
static class NameTreadFactory implements ThreadFactory {
private final AtomicInteger mThreadNum = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "my-thread-" + mThreadNum.getAndIncrement());
System.out.println(t.getName() + " has been created");
return t;
}
} public static class MyIgnorePolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
doLog(r, e);
}
private void doLog(Runnable r, ThreadPoolExecutor e) {
// 可做日志记录等
System.err.println( r.toString() + " rejected");
// System.out.println("completedTaskCount: " + e.getCompletedTaskCount());
}
} static class MyTask implements Runnable {
private String name;
public MyTask(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println(this.toString() + " is running!");
Thread.sleep(3000); //让任务执行慢点
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getName() {
return name;
}
@Override
public String toString() {
return "MyTask [name=" + name + "]";
}
}
}

运行结果:

其中7-10号线程被拒绝策略拒绝了,1、2号线程执行完后,3、6号线程进入核心线程池执行,此时4、5号线程在任务队列等待执行,3、6线程执行完再通知4、5线程执行

java自带的四种线程池的更多相关文章

  1. Java通过Executors提供四种线程池

    http://cuisuqiang.iteye.com/blog/2019372 Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果 ...

  2. Java中常用的四种线程池

    在Java中使用线程池,可以用ThreadPoolExecutor的构造函数直接创建出线程池实例,如何使用参见之前的文章Java线程池构造参数详解.不过,在Executors类中,为我们提供了常用线程 ...

  3. Java四种线程池的使用

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  4. Java 四种线程池的使用

    java线程线程池监控 Java通过Executors提供四种线程池,分别为: newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收 ...

  5. Java四种线程池的学习与总结

    在Java开发中,有时遇到多线程的开发时,直接使用Thread操作,对程序的性能和维护上都是一个问题,使用Java提供的线程池来操作可以很好的解决问题. 一.new Thread的弊端 执行一个异步任 ...

  6. 【转】Java四种线程池的使用

    Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...

  7. 线程池是什么?Java四种线程池的使用介绍

    使用线程池的好处有很多,比如节省系统资源的开销,节省创建和销毁线程的时间等,当我们需要处理的任务较多时,就可以使用线程池,可能还有很多用户不知道Java线程池如何使用?下面小编给大家分享Java四种线 ...

  8. Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...

  9. Java四种线程池

    Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...

随机推荐

  1. hanoi(老汉诺塔问题新思维)

    #include <stdio.h> //第一个塔为初始塔,中间的塔为借用塔,最后一个塔为目标塔 int i=1;//记录步数 void move(int n, char from,cha ...

  2. nacos底层原理

    Nacos   为什么选择NacosNacos 致力于帮助您发现.配置和管理微服务.Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现.服务配置.服务元数据及流量管理. Nacos 帮 ...

  3. Scanner几个问题与正则简介

    Pre:最近做了头条的在线笔试,对Scanner输入的处理有些特殊,当时是一脸懵逼态,遂由此随笔(/@_@\),java小白,有错难免! 查了下Scanner的源码,没有头绪,但是其中用到了正则的知识 ...

  4. Switch语句的条件只能接受什么类型的值

    switch语句只能针对基本数据类型使用,这些类型包括int.char.枚举.bool等.对于其他类型,则必须使用if语句. 在一个 switch 中可以有任意数量的 case 语句.每个 case ...

  5. JVM 选项 -XX:+UseCompressedOops 有什么作用? 为什么要使用?

    当你将你的应用从 32 位的 JVM 迁移到 64 位的 JVM 时,由于对象的指针从 32 位增加到了 64 位,因此堆内存会突然增加,差不多要翻倍.这也会对 CPU 缓存(容量比内存小很多)的数据 ...

  6. Flask-Script使用教程

    Flask使用第三方脚本 一个干净的项目准备: 一个干净的Flask项目连接地址: https://pan.baidu.com/s/123TyVXOFvh5P7V8MbyMfDg 话不多说,上菜: 1 ...

  7. springboot使用策略模式实现一个基本的促销

    策略模式 定义了算法族,分别封装起来,让它们之间可以互相替换, 此模式让算法的变化独立于使用算法的客户 源码:https://github.com/youxiu326/sb_promotion.git ...

  8. C语言中的 @ 符号是什么意思?

    Global Variable Address Modifier (@address)You can assign global variables to specific addresses wit ...

  9. jupyter notebook使用技巧

    shift + tab 键可以查看对应源代码(注意:需要先将代码运行才能查看) Jupyter Notebook 的快捷键 Jupyter Notebook 有两种键盘输入模式:1.命令模式,键盘输入 ...

  10. 实现自定义的小程序底部tabbar

    背景 诶,当然是为了实现更有温度的代码啦(背后设计师拿着刀对着我) 自带tabbar app.json中配置: tabBar: { backgroundColor: '#fff', borderSty ...