Java 自定义线程池和线程总数控制操作

1 概述

池化是常见的思想,线程池是非常典型的池化的实现,《Java并发编程实战》也大篇幅去讲解了Java中的线程池。本文实现一个简单的线程池。

2 核心类

【1】接口定义

public interface IThreadPool<Job extends Runnable> {
 /**
 * 关闭线程池
 */
 public void shutAlldown();

 /**
 * 执行任务
 *
 * @param job 任务
 */
 public void execute(Job job);

 /**
 * 添加工作者
 *
 * @param addNum 添加数
 */
 public void addWorkers(int addNum);

 /**
 * 减少工作者
 *
 * @param reduceNum 减少数目
 */
 public void reduceWorkers(int reduceNum);
}

【2】实现类

线程池的核心是维护了1个任务列表和1个工作者列表。

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class XYThreadPool<Job extends Runnable> implements IThreadPool<Job> {
 // 默认线程数
 private static int DEAFAULT_SIZE = 5;
 // 最大线程数
 private static int MAX_SIZE = 10;
 // 任务列表
 private LinkedList<Job> tasks = new LinkedList<Job>();
 // 工作线程列表
 private List<Worker> workers = Collections
  .synchronizedList(new ArrayList<Worker>());
 /**
 * 默认构造函数
 */
 public XYThreadPool() {
 initWokers(DEAFAULT_SIZE);
 }
 /**
 * 执行线程数
 *
 * @param threadNums 线程数
 */
 public XYThreadPool(int workerNum) {
 workerNum = workerNum <= 0 ? DEAFAULT_SIZE
  : workerNum > MAX_SIZE ? MAX_SIZE : workerNum;
 initWokers(workerNum);
 }
 /**
 * 初始化线程池
 *
 * @param threadNums 线程数
 */
 public void initWokers(int threadNums) {
 for (int i = 0; i < threadNums; i++) {
  Worker worker = new Worker();
  worker.start();
  workers.add(worker);
 }
 // 添加关闭钩子
 Runtime.getRuntime().addShutdownHook(new Thread() {
  public void run() {
  shutAlldown();
  }
 });
 }
 @Override
 public void shutAlldown() {
 for (Worker worker : workers) {
  worker.shutdown();
 }
 }
 @Override
 public void execute(Job job) {
 synchronized (tasks) {
  // 提交任务就是将任务对象加入任务队列,等待工作线程去处理
  tasks.addLast(job);
  tasks.notifyAll();
 }
 }
 @Override
 public void addWorkers(int addNum) {
 // 新线程数必须大于零,并且线程总数不能大于最大线程数
 if ((workers.size() + addNum) <= MAX_SIZE && addNum > 0) {
  initWokers(addNum);
 } else {
  System.out.println("addNum too large");
 }
 }
 @Override
 public void reduceWorkers(int reduceNum) {
 if ((workers.size() - reduceNum <= 0))
  System.out.println("thread num too small");
 else {
  // 暂停指定数量的工作者
  int count = 0;
  while (count != reduceNum) {
  for (Worker w : workers) {
   w.shutdown();
   count++;
  }
  }
 }
 }
 /**
 * 工作线程
 */
 class Worker extends Thread {
 private volatile boolean flag = true;
 @Override
 public void run() {
  while (flag) {
  Job job = null;
  // 加锁(若只有一个woker可不必加锁,那就是所谓的单线程的线程池,线程安全)
  synchronized (tasks) {
   // 任务队列为空
   while (tasks.isEmpty()) {
   try {
    // 阻塞,放弃对象锁,等待被notify唤醒
    tasks.wait();
    System.out.println("block when tasks is empty");
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   }
   // 不为空取出任务
   job = tasks.removeFirst();
   System.out.println("get job:" + job + ",do biz");
   job.run();
  }
  }
 }
 public void shutdown() {
  flag = false;
 }
 }
}

(1) 当调用wait()方法时线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备

(2) Object的方法:void notify(): 唤醒一个正在等待该对象的线程。void notifyAll(): 唤醒所有正在等待该对象的线程。

notifyAll使所有原来在该对象上等待被notify的线程统统退出wait状态,变成等待该对象上的锁,一旦该对象被解锁,它们会去竞争。

notify只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其它同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。

3 无需控制线程总数

每调用一次就会创建一个拥有10个线程工作者的线程池。

public class TestService1 {
 public static void main(String[] args) {
 // 启动10个线程
 XYThreadPool<Runnable> pool = new XYThreadPool<Runnable>(10);
 pool.execute(new Runnable() {
  @Override
  public void run() {
  System.out.println("====1 test====");
  }
 });
 }
}
public class TestService2 {
 public static void main(String[] args) {
 // 启动10个线程
 XYThreadPool<Runnable> pool = new XYThreadPool<Runnable>(10);
 pool.execute(new Runnable() {
  @Override
  public void run() {
  System.out.println("====2 test====");
  }
 });
 }
}

4 控制线程总数

在项目中所有的线程调用,一般都共用1个固定工作者数大小的线程池。

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.xy.pool.XYThreadPool;
/**
 * 统一线程池管理类
 */
@Component
public class XYThreadManager {
 private XYThreadPool<Runnable> executorPool;
 @PostConstruct
 public void init() {
 executorPool = new XYThreadPool<Runnable>(10);
 }
 public XYThreadPool<Runnable> getExecutorPool() {
 return executorPool;
 }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("testService3")
public class TestService3 {
 @Autowired
 private XYThreadManager threadManager;
 public void test() {
 threadManager.getExecutorPool().execute(new Runnable() {
  @Override
  public void run() {
  System.out.println("====3 test====");
  }
 });
 }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service("testService4")
public class TestService4 {
 @Autowired
 private XYThreadManager threadManager;
 public void test() {
 threadManager.getExecutorPool().execute(new Runnable() {
  @Override
  public void run() {
  System.out.println("====4 test====");
  }
 });
 }
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestMain {
 @SuppressWarnings("resource")
 public static void main(String[] args) {
 ApplicationContext atc = new ClassPathXmlApplicationContext("applicationContext.xml");
 TestService3 t3 = (TestService3) atc.getBean("testService3");
 t3.test();
 TestService4 t4 = (TestService4) atc.getBean("testService4");
 t4.test();
 }
}

补充:论如何优雅的自定义ThreadPoolExecutor线程池

前言

线程池想必大家也都用过,JDK的Executors 也自带一些线程池。但是不知道大家有没有想过,如何才是最优雅的方式去使用过线程池吗? 生产环境要怎么去配置自己的线程池才是合理的呢?

今天周末,刚好有时间来总结一下自己所认为的'优雅', 如有问题欢迎大家指正。

线程池使用规则

要使用好线程池,那么一定要遵循几个规则:

线程个数大小的设置

线程池相关参数配置

利用Hook嵌入你的行为

线程池的关闭

线程池配置相关

线程池大小的设置

这其实是一个面试的考点,很多面试官会问你线程池coreSize 的大小来考察你对于线程池的理解。

首先针对于这个问题,我们必须要明确我们的需求是计算密集型还是IO密集型,只有了解了这一点,我们才能更好的去设置线程池的数量进行限制。

1、计算密集型:

顾名思义就是应用需要非常多的CPU计算资源,在多核CPU时代,我们要让每一个CPU核心都参与计算,将CPU的性能充分利用起来,这样才算是没有浪费服务器配置,如果在非常好的服务器配置上还运行着单线程程序那将是多么重大的浪费。对于计算密集型的应用,完全是靠CPU的核数来工作,所以为了让它的优势完全发挥出来,避免过多的线程上下文切换,比较理想方案是:

线程数 = CPU核数+1,也可以设置成CPU核数*2,但还要看JDK的版本以及CPU配置(服务器的CPU有超线程)。

一般设置CPU * 2即可。

2、IO密集型

我们现在做的开发大部分都是WEB应用,涉及到大量的网络传输,不仅如此,与数据库,与缓存间的交互也涉及到IO,一旦发生IO,线程就会处于等待状态,当IO结束,数据准备好后,线程才会继续执行。因此从这里可以发现,对于IO密集型的应用,我们可以多设置一些线程池中线程的数量,这样就能让在等待IO的这段时间内,线程可以去做其它事,提高并发处理效率。那么这个线程池的数据量是不是可以随便设置呢?当然不是的,请一定要记得,线程上下文切换是有代价的。目前总结了一套公式,对于IO密集型应用:

线程数 = CPU核心数/(1-阻塞系数) 这个阻塞系数一般为0.8~0.9之间,也可以取0.8或者0.9。

套用公式,对于双核CPU来说,它比较理想的线程数就是20,当然这都不是绝对的,需要根据实际情况以及实际业务来调整:final int poolSize = (int)(cpuCore/(1-0.9))

针对于阻塞系数,《Programming Concurrency on the JVM Mastering》即《Java 虚拟机并发编程》中有提到一句话:

对于阻塞系数,我们可以先试着猜测,抑或采用一些细嫩分析工具或java.lang.management API 来确定线程花在系统/IO操作上的时间与CPU密集任务所耗的时间比值。

线程池相关参数配置

说到这一点,我们只需要谨记一点,一定不要选择没有上限限制的配置项。

这也是为什么不建议使用Executors 中创建线程的方法。

比如,Executors.newCachedThreadPool的设置与无界队列的设置因为某些不可预期的情况,线程池会出现系统异常,导致线程暴增的情况或者任务队列不断膨胀,内存耗尽导致系统崩溃和异常。 我们推荐使用自定义线程池来避免该问题,这也是在使用线程池规范的首要原则! 小心无大错,千万别过度自信!

可以看下Executors中四个创建线程池的方法:

//使用无界队列
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                   0L, TimeUnit.MILLISECONDS,
                   new LinkedBlockingQueue<Runnable>());
  }

//线程池数量是无限的
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                   60L, TimeUnit.SECONDS,
                   new SynchronousQueue<Runnable>());
  }

其他的就不再列举了,大家可以自行查阅源码。

第二,合理设置线程数量、和线程空闲回收时间,根据具体的任务执行周期和时间去设定,避免频繁的回收和创建,虽然我们使用线程池的目的是为了提升系统性能和吞吐量,但是也要考虑下系统的稳定性,不然出现不可预期问题会很麻烦!

第三,根据实际场景,选择适用于自己的拒绝策略。进行补偿,不要乱用JDK支持的自动补偿机制!尽量采用自定义的拒绝策略去进行兜底!

第四,线程池拒绝策略,自定义拒绝策略可以实现RejectedExecutionHandler接口。

JDK自带的拒绝策略如下:

AbortPolicy:直接抛出异常阻止系统正常工作。

CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。

DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务。

DiscardPolicy:丢弃无法处理的任务,不给予任何处理。

利用Hook

利用Hook,留下线程池执行轨迹:

ThreadPoolExecutor提供了protected类型可以被覆盖的钩子方法,允许用户在任务执行之前会执行之后做一些事情。我们可以通过它来实现比如初始化ThreadLocal、收集统计信息、如记录日志等操作。这类Hook如beforeExecute和afterExecute。另外还有一个Hook可以用来在任务被执行完的时候让用户插入逻辑,如rerminated 。

如果hook方法执行失败,则内部的工作线程的执行将会失败或被中断。

我们可以使用beforeExecute和afterExecute来记录线程之前前和后的一些运行情况,也可以直接把运行完成后的状态记录到ELK等日志系统。

关闭线程池

内容当线程池不在被引用并且工作线程数为0的时候,线程池将被终止。我们也可以调用shutdown来手动终止线程池。如果我们忘记调用shutdown,为了让线程资源被释放,我们还可以使用keepAliveTime和allowCoreThreadTimeOut来达到目的!

当然,稳妥的方式是使用虚拟机Runtime.getRuntime().addShutdownHook方法,手工去调用线程池的关闭方法!

线程池使用实例

线程池核心代码:

public class AsyncProcessQueue {
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /**
 * Task 包装类<br>
 * 此类型的意义是记录可能会被 Executor 吃掉的异常<br>
 */
 public static class TaskWrapper implements Runnable {
 private static final Logger _LOGGER = LoggerFactory.getLogger(TaskWrapper.class);
 private final Runnable gift;
 public TaskWrapper(final Runnable target) {
  this.gift = target;
 }
 @Override
 public void run() {

  // 捕获异常,避免在 Executor 里面被吞掉了
  if (gift != null) {

  try {
   gift.run();
  } catch (Exception e) {
   _LOGGER.error("Wrapped target execute exception.", e);
  }
  }
 }
 }
 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 /**
 * 执行指定的任务
 *
 * @param task
 * @return
 */
 public static boolean execute(final Runnable task) {
 return AsyncProcessor.executeTask(new TaskWrapper(task));
 }
}
public class AsyncProcessor {
 static final Logger LOGGER = LoggerFactory.getLogger(AsyncProcessor.class);

 /**
 * 默认最大并发数<br>
 */
 private static final int DEFAULT_MAX_CONCURRENT = Runtime.getRuntime().availableProcessors() * 2;

 /**
 * 线程池名称格式
 */
 private static final String THREAD_POOL_NAME = "ExternalConvertProcessPool-%d";

 /**
 * 线程工厂名称
 */
 private static final ThreadFactory FACTORY = new BasicThreadFactory.Builder().namingPattern(THREAD_POOL_NAME)
  .daemon(true).build();

 /**
 * 默认队列大小
 */
 private static final int DEFAULT_SIZE = 500;

 /**
 * 默认线程存活时间
 */
 private static final long DEFAULT_KEEP_ALIVE = 60L;

 /**NewEntryServiceImpl.java:689
 * Executor
 */
 private static ExecutorService executor;

 /**
 * 执行队列
 */
 private static BlockingQueue<Runnable> executeQueue = new ArrayBlockingQueue<>(DEFAULT_SIZE);
 static {
 // 创建 Executor
 // 此处默认最大值改为处理器数量的 4 倍
 try {
  executor = new ThreadPoolExecutor(DEFAULT_MAX_CONCURRENT, DEFAULT_MAX_CONCURRENT * 4, DEFAULT_KEEP_ALIVE,
   TimeUnit.SECONDS, executeQueue, FACTORY);
  // 关闭事件的挂钩
  Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
  @Override
  public void run() {
   AsyncProcessor.LOGGER.info("AsyncProcessor shutting down.");
   executor.shutdown();
   try {
   // 等待1秒执行关闭
   if (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
    AsyncProcessor.LOGGER.error("AsyncProcessor shutdown immediately due to wait timeout.");
    executor.shutdownNow();
   }
   } catch (InterruptedException e) {
   AsyncProcessor.LOGGER.error("AsyncProcessor shutdown interrupted.");
   executor.shutdownNow();
   }
   AsyncProcessor.LOGGER.info("AsyncProcessor shutdown complete.");
  }
  }));
 } catch (Exception e) {
  LOGGER.error("AsyncProcessor init error.", e);
  throw new ExceptionInInitializerError(e);
 }
 }
 /**
 * 此类型无法实例化
 */
 private AsyncProcessor() {
 }
 /**
 * 执行任务,不管是否成功<br>
 * 其实也就是包装以后的 {@link Executer} 方法
 *
 * @param task
 * @return
 */
 public static boolean executeTask(Runnable task) {
 try {
  executor.execute(task);
 } catch (RejectedExecutionException e) {
  LOGGER.error("Task executing was rejected.", e);
  return false;
 }
 return true;
 }
 /**
 * 提交任务,并可以在稍后获取其执行情况<br>
 * 当提交失败时,会抛出 {@link }
 *
 * @param task
 * @return
 */
 public static <T> Future<T> submitTask(Callable<T> task) {
 try {
  return executor.submit(task);
 } catch (RejectedExecutionException e) {
  LOGGER.error("Task executing was rejected.", e);
  throw new UnsupportedOperationException("Unable to submit the task, rejected.", e);
 }
 }
}

使用方式:

AsyncProcessQueue.execute(new Runnable() {
     @Override
     public void run() {
        //do something
    }
});

可以根据自己的使用场景灵活变更,我这里并没有用到beforeExecute和afterExecute以及拒绝策略。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • java并发包中CountDownLatch和线程池的使用详解

    1.CountDownLatch 现在做的这个华为云TaurusDB比赛中,参考的之前参加过阿里的PolarDB大赛的两个大佬的代码,发现都有用到CountDownLatch这个类,之前看代码的时候也看过,但是没有搞得很明白,自己写也写不出来,在此自己先学习一下. 字面理解:CountDownLatch:数量减少的门栓. 创建这样一个门栓 CountDownLatch countDownLatch = new CountDownLatch(count); 参数:count,门栓的计数次数. 在所

  • java 线程池keepAliveTime的含义说明

    之前对线程池中属性:keepAliveTime比较模糊,而且看过之后过一段时间就会忘掉,于是就在此记录一下. keepAliveTime的jdk中的解释为: 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间. 说的让人感觉比较模糊,总结一下大概意思为:比如说线程池中最大的线程数为50,而其中只有40个线程任务在跑,相当于有10个空闲线程,这10个空闲线程不能让他一直在开着,因为线程的存在也会特别好资源的,所有就需要设置一个这个空闲线程的存活时间,这么解释应该就很清楚了. 这样以后

  • java多线程CountDownLatch与线程池ThreadPoolExecutor/ExecutorService案例

    1.CountDownLatch: 一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行. 2.ThreadPoolExecutor/ExecutorService: 线程池,使用线程池可以复用线程,降低频繁创建线程造成的性能消耗,同时对线程的创建.启动.停止.销毁等操作更简便. 3.使用场景举例: 年末公司组织团建,要求每一位员工周六上午8点到公司门口集合,统一乘坐公司所租大巴前往目的地. 在这个案例中,公司作为主线程,员工作为子线程. 4.代码示例: package

  • Java 线程池的作用以及该如何使用

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线程,虽然这种方法似乎易于实现,但它有重大缺点.为每个请求创建新线程将花费更多的时间,在创建和销毁线程时花费更多的系统资源.因此同时创建太多线程的 JVM 可能会导致系统内存不足,这就需要限制要创建的线程数,也就是需要使用到线程池. 一.什么是 Java 中的线程池? 线程池技术就是线程的重用技术,使

  • 教你如何监控 Java 线程池运行状态的操作(必看)

    之前写过一篇 Java 线程池的使用介绍文章<线程池全面解析>,全面介绍了什么是线程池.线程池核心类.线程池工作流程.线程池分类.拒绝策略.及如何提交与关闭线程池等. 但在实际开发过程中,在线程池使用过程中可能会遇到各方面的故障,如线程池阻塞,无法提交新任务等. 如果你想监控某一个线程池的执行状态,线程池执行类 ThreadPoolExecutor 也给出了相关的 API, 能实时获取线程池的当前活动线程数.正在排队中的线程数.已经执行完成的线程数.总线程数等. 总线程数 = 排队线程数 +

  • java高级应用:线程池的全面讲解(干货)

    什么是线程池? 很简单,简单看名字就知道是装有线程的池子,我们可以把要执行的多线程交给线程池来处理,和连接池的概念一样,通过维护一定数量的线程池来达到多个线程的复用. 线程池的好处 我们知道不用线程池的话,每个线程都要通过new Thread(xxRunnable).start()的方式来创建并运行一个线程,线程少的话这不会是问题,而真实环境可能会开启多个线程让系统和程序达到最佳效率,当线程数达到一定数量就会耗尽系统的CPU和内存资源,也会造成GC频繁收集和停顿,因为每次创建和销毁一个线程都是要

  • Java 自定义线程池和线程总数控制操作

    1 概述 池化是常见的思想,线程池是非常典型的池化的实现,<Java并发编程实战>也大篇幅去讲解了Java中的线程池.本文实现一个简单的线程池. 2 核心类 [1]接口定义 public interface IThreadPool<Job extends Runnable> { /** * 关闭线程池 */ public void shutAlldown(); /** * 执行任务 * * @param job 任务 */ public void execute(Job job);

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

    ThreadPoolExecutor简介 ThreadPoolExecutor是线程池类.对于线程池,可以通俗的将它理解为"存放一定数量线程的一个线程集合.线程池允许若个线程同时允许,允许同时运行的线程数量就是线程池的容量:当添加的到线程池中的线程超过它的容量时,会有一部分线程阻塞等待.线程池会通过相应的调度策略和拒绝策略,对添加到线程池中的线程进行管理." ThreadPoolExecutor数据结构 ThreadPoolExecutor的数据结构如下图所示: 各个数据在Thread

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

    线程池示例 在分析线程池之前,先看一个简单的线程池示例. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class ThreadPoolDemo1 { public static void main(String[] args) { // 创建一个可重用固定线程数的线程池 ExecutorService pool = Executors.newFixedThre

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

    拒绝策略介绍 线程池的拒绝策略,是指当任务添加到线程池中被拒绝,而采取的处理措施. 当任务添加到线程池中之所以被拒绝,可能是由于:第一,线程池异常关闭.第二,任务数量超过线程池的最大限制. 线程池共包括4种拒绝策略,它们分别是:AbortPolicy, CallerRunsPolicy, DiscardOldestPolicy和DiscardPolicy. AbortPolicy         -- 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException

  • python自定义线程池控制线程数量的示例

    1.自定义线程池 import threading import Queue import time queue = Queue.Queue() def put_data_in_queue(): for i in xrange(10): queue.put(i) class MyThread(threading.Thread): def run(self): while not queue.empty(): sleep_times = queue.get() time.sleep(sleep_t

  • java线程池中线程数量到底是几

    目录 线程池配置 线程池里的业务线程数量小于最小数量(5) 第一个请求 第二个请求 第三个请求 第五个请求 小于阻塞队列容量(10) 第六个请求 第七个请求 第15个请求 小于最大数量(20) 第16个请求 第35个请求 拒绝策略 第36个请求 复用线程 线程池配置 线程池配置,假设是: 1.最小数量是5 2.阻塞队列容量是10 3.最大数量是20 线程池里的业务线程数量小于最小数量(5) 第一个请求 第一个请求进来的时候,这个时候,线程池没有线程,就创建新的工作线程(即Worker线程). 然

  • Java线程池复用线程的秘密你知道吗

    目录 前言 源码探究 execute方法 addWorker方法 Worker类 实现了Runnable接口 重要属性 构造方法 run方法 执行流程 总结 前言 我们都知道线程池可以帮我们管理线程,重复利用线程执行不同的任务.正常情况下,我们创建的线程执行完任务后就会自行销毁,那么线程池是如何做到复用线程的呢? 源码探究 我们从线程池ThreadPoolExecutor源码入手,一探究竟.为了突出重点,以下的方法源码过滤了部分无关代码,以求逻辑清晰. execute方法 那就从线程池执行的ex

  • Springboot 配置线程池创建线程及配置 @Async 异步操作线程池详解

    目录 前言 一.创建一个Springboot Web项目 二.新建ThreadPoolConfig 三.新建controller测试 四.演示结果 前言 众所周知,创建显示线程和直接使用未配置的线程池创建线程,都会被阿里的大佬给diss,所以我们要规范的创建线程. 至于 @Async 异步任务的用处是不想等待方法执行完就返回结果,提高软件前台响应速度,一个程序中会用到很多异步方法,所以需要使用线程池管理,防止影响性能. 一.创建一个Springboot Web项目 需要一个Springboot项

  • @Async异步线程池以及线程的命名方式

    本文记录@Async的基本使用以及通过实现ThreadFactory来实现对线程的命名. @Async的基本使用 近日有一个道友提出到一个问题,大意如下: 业务场景需要进行批量更新,已有数据id主键.更新的状态.单条更新性能太慢,所以使用in进行批量更新.但是会导致锁表使得其他业务无法访问该表,in的量级太低又导致性能太慢. 龙道友提出了一个解决方案,把要处理的数据分成几个list之后使用多线程进行数据更新.提到多线程可直接使用@Async注解来进行异步操作. 好的,接下来上面的问题我们不予解答

  • Python实现线程池之线程安全队列

    目录 一.线程池组成 二.线程安全队列的实现 三.测试逻辑 3.1.测试阻塞逻辑 3.2.测试读写加锁逻辑 本文实例为大家分享了Python实现线程池之线程安全队列的具体代码,供大家参考,具体内容如下 一.线程池组成 一个完整的线程池由下面几部分组成,线程安全队列.任务对象.线程处理对象.线程池对象.其中一个线程安全的队列是实现线程池和任务队列的基础,本节我们通过threading包中的互斥量threading.Lock()和条件变量threading.Condition()来实现一个简单的.读

随机推荐