讲解线程池的一篇干货,很干很干!

搜索引擎——ElasticSearch汇总 汇总——ElasticSearch篇 全文检索 问题1:什么是全文检索? 基本概念:对**非结构化数据/结构化数据**先建立**索引**,再对索引进行搜索**文档**的过程叫做全文检索。 什么是非结构化与结构化数据? - 非结构化数据:没有固定的格式的数据(html,word,excel)---没有固定长度,字段约束,描述,散列数据 - 结构化数据:有固定的格式的数据(如:数据库数据)---有固定长度(varchar(50)),字段类型,描述修饰数据 搜索算法:顺序扫描法、倒排索引法 - 顺序 阅读详情

文章部分内容以及图片引用自公众号:crossoverJie

前言

平时接触过多线程开发的童鞋应该都或多或少了解过线程池,之前发布的《阿里巴巴 Java 手册》里也有一条:

可见线程池的重要性。

简单来说使用线程池有以下几个目的:

  • 线程是稀缺资源,不能频繁的创建。

  • 解耦作用;线程创建于执行完全分开,方便维护

  • 应当将其放入一个池子中,可以给其他任务进行复用。

 

线程池原理

谈到线程池就会想到池化技术,其中最核心的思想就是把宝贵的资源放到一个池子中;每次使用都从里面获取,用完之后又放回池子供其他人使用,有点吃大锅饭的意思。

那在 Java 中又是如何实现的呢?

在 JDK 1.5 之后推出了相关的 api,常见的创建线程池方式有以下几种:

  • Executors.newCachedThreadPool():无限线程池。

  • Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。

  • Executors.newSingleThreadExecutor():创建单个线程的线程池。

 

其实看这三种方式创建的源码就会发现:

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue());
    }

实际上还是利用 ThreadPoolExecutor 类实现的。

所以我们重点来看下:

ThreadPoolExecutor 是怎么玩的。

 

首先是创建线程的 api:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) 

这几个核心参数的作用:

corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几个阻塞队列。
ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
PriorityBlockingQueue:一个具有优先级得无限阻塞队列。
maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常又帮助。
RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
ThreadPoolExecutor.AbortPolicy:
            当线程池中的数量等于最大线程数时抛 java.util.concurrent.RejectedExecutionException 异常,涉及到该异常的任务              也不会被执行,线程池默认的拒绝策略就是该策略。

        2. ThreadPoolExecutor.DiscardPolicy():

            当线程池中的数量等于最大线程数时,默默丢弃不能执行的新加任务,不报任何异常。

        3. ThreadPoolExecutor.CallerRunsPolicy():

            当线程池中的数量等于最大线程数时,重试添加当前的任务;它会自动重复调用execute()方法。

        4. ThreadPoolExecutor.DiscardOldestPolicy():

            当线程池中的数量等于最大线程数时,抛弃线程池中工作队列头部的任务(即等待时间最久的任务),并执行当前任务。

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。

workQueue 用于存放任务的阻塞队列。

handler 当队列和最大线程池都满了之后的饱和策略。

通常我们都是这样使用demo:

@Component
public class ExecutorThreadPools {

	
	BlockingQueue<Runnable> executeReportQueue = new LinkedBlockingQueue<Runnable>(10);
    ThreadPoolExecutor executeReportPool = new ThreadPoolExecutor(5, 15, 10L,TimeUnit.MINUTES, executeReportQueue, new ThreadPoolExecutor.CallerRunsPolicy());
    
    public ThreadPoolExecutor getExecuteReportPool() {
		return executeReportPool;
	}
	
}

了解了这几个参数再来看看实际的运用。

通常我们都是使用:

 

threadPool.execute(new Job());

这样的方式来提交一个任务到线程池中,所以核心的逻辑就是 execute() 函数了。

 

在具体分析之前先了解下线程池中所定义的状态,这些状态都和线程的执行密切相关:

  • RUNNING 自然是运行状态,指可以接受任务执行队列里的任务

 

  • SHUTDOWN 指调用了 shutdown() 方法,不再接受新任务了,但是队列里的任务得执行完毕。

 

  • STOP 指调用了 shutdownNow() 方法,不再接受新任务,同时抛弃阻塞队列里的所有任务并中断所有正在执行任务。

 

  • TIDYING 所有任务都执行完毕,在调用 shutdown()/shutdownNow() 中都会尝试更新为这个状态。

 

  • TERMINATED 终止状态,当执行 terminated() 后会更新为这个状态。

 

用图表示为:

然后看看 execute() 方法是如何处理的:

  • 获取当前线程池的状态。

 

  • 当前线程数量小于 coreSize 时创建一个新的线程运行。

 

  • 如果当前线程处于运行状态,并且写入阻塞队列成功。

 

  • 双重检查,再次获取线程状态;如果线程状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。

 

  • 如果当前线程池为空就新创建一个线程并执行。

 

  • 如果在第三步的判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略。

 

这里借助《聊聊并发》的一张图来描述这个流程:

如何配置线程?

流程聊完了再来看看上文提到了几个核心参数应该如何配置呢?

有一点是肯定的,线程池肯定是不是越大越好。

通常我们是需要根据这批任务执行的性质来确定的。

  • IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2

  • CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。

当然这些都是经验值,最好的方式还是根据实际情况测试得出最佳配置。

 

优雅的关闭线程池

有运行任务自然也有关闭任务,从上文提到的 5 个状态就能看出如何来关闭线程池。

其实无非就是两个方法:

shutdown()/shutdownNow()。

 

但他们有着重要的区别:

  • shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。

  • shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。

两个方法都会中断线程,用户可自行判断是否需要响应中断。

 

shutdownNow() 要更简单粗暴,可以根据实际场景选择不同的方法。

我通常是按照以下方式关闭线程池的:

 long start = System.currentTimeMillis();
        for (int i = 0; i <= 5; i++) {
            pool.execute(new Job());
        }

        pool.shutdown();

        while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
            LOGGER.info("线程还在执行。。。");
        }
        long end = System.currentTimeMillis();
        LOGGER.info("一共处理了【{}】", (end - start));

pool.awaitTermination(1, TimeUnit.SECONDS) 会每隔一秒钟检查一次是否执行完毕(状态为 TERMINATED),当从 while 循环退出时就表明线程池已经完全终止了。

 

SpringBoot中使用线程池

来看看springboot中怎么配置和使用线程池:

既然用了 SpringBoot ,那自然得发挥 Spring 的特性,所以需要 Spring 来帮我们管理线程池:

@Component
public class ExecutorThreadPools {

	
	BlockingQueue<Runnable> executeReportQueue = new LinkedBlockingQueue<Runnable>(10);
    ThreadPoolExecutor executeReportPool = new ThreadPoolExecutor(5, 15, 10L,TimeUnit.MINUTES, executeReportQueue, new ThreadPoolExecutor.CallerRunsPolicy());
    
    public ThreadPoolExecutor getExecuteReportPool() {
		return executeReportPool;
	}
	
}

自定义一个可定制线程的子类:

public class ConfigurableThread extends Thread {
	
	private boolean firstExe = false;
	private boolean enableTimeLimit = false;
	private boolean enableExeLimit = false;
	private Date firstExeTime;
	private Integer exeLimit = 0;
	private Long timeLimit = 0L;
	private Long increaseStep = 0L;
	
	protected Long timeInterval = 1000L;
	protected Integer tryCount = 1;
	protected boolean successFlag = false; 
	
	/**
	 * 执行逻辑主体
	 */
	@Override
	public void run(){
		if(!firstExe){
			this.firstExe = true;
			this.firstExeTime = new Date();
		}
		try {
			while(execuble()){
				doRun();
				tryCount ++;
				if(!successFlag) {
					Thread.sleep(this.timeInterval);
				}
				this.timeInterval += this.increaseStep;
			}
			return;
		} catch (Exception e) {
			threadExceptionHandle(e);
		}
	}
	
	/**
	 * 子类必须重写该方法
	 * @Description 具体业务相关的行为放在这里
	 * @throws Exception
	 */
	protected void doRun() throws Exception{
		throw new Exception("还未声明实现方法");
	}
	
	/**
	 * 超时处理
	 */
	protected void onTimeout() {
		System.out.println("执行时间超时,线程结束");
	}
	
	/**
	 * 重试次数上限处理
	 */
	protected void onTryout() {
		System.out.println("尝试次数超出,线程结束");
	}
	
	/**
	 * 成功处理
	 */
	protected void onSuccess() {
		System.out.println("任务执行成功,线程结束");
	}
	
	/**
	 * run方法的异常处理
	 */
	protected void threadExceptionHandle(Exception e) {
		System.out.println("线程执行错误,程序退出.错误信息:"+e);
	}
	
	private boolean execuble(){
		if(successFlag){
			onSuccess();
			return false;
		}
		
		if(enableTimeLimit && 
				(System.currentTimeMillis() - this.firstExeTime.getTime()) > this.timeLimit){
			onTimeout();
			return false;
		}
		
		if(enableExeLimit && 
				this.tryCount > this.exeLimit){
			onTryout();
			return false;
		}
		return true;
	}
	
	protected final void markAsSuccess(){
		this.successFlag = true;
	}
	
	public final ConfigurableThread enableTimeLimit() {
		this.enableTimeLimit = true;
		return this;
	}
	
	public final ConfigurableThread disableTimeLimit() {
		this.enableTimeLimit = false;
		return this;
	}
	
	public final ConfigurableThread enableExeLimit() {
		this.enableExeLimit = true;
		return this;
	}
	
	public final ConfigurableThread disableExeLimit() {
		this.enableExeLimit = false;
		return this;
	}

	public final ConfigurableThread setExeLimit(Integer exeLimit) {
		this.exeLimit = exeLimit;
		return this;
	}


	public final ConfigurableThread setTimeLimit(Long timeLimit) {
		this.timeLimit = timeLimit;
		return this;
	}
	
	public final ConfigurableThread setInterval(Long interval) {
		this.timeInterval = interval;
		return this;
	}
	
	public final ConfigurableThread setIncreaseStep(Long step) {
		this.increaseStep = step;
		return this;
	}

}

使用时:

使用时也挺简单,注入线程池,新创建一个线程重写doRun()方法执行逻辑主体,并执行excute()方法。(上述线程池的使用方式解决了Spring在Thread中注入bean无效的解决方式;若直接在Thread中直接注入的Bean是null,会发生空指针错误)

 

监控线程池

谈到了 SpringBoot,也可利用它 actuator 组件来做线程池的监控。

线程怎么说都是稀缺资源,对线程池的监控可以知道自己任务执行的状况、效率等。

关于 actuator 就不再细说了,感兴趣的可以看看这篇,有详细整理过如何暴露监控端点。

其实 ThreadPool 本身已经提供了不少 api 可以获取线程状态:

很多方法看名字就知道其含义,只需要将这些信息暴露到 SpringBoot 的监控端点中,我们就可以在可视化页面查看当前的线程池状态了。

甚至我们可以继承线程池扩展其中的几个函数来自定义监控逻辑:

看这些名称和定义都知道,这是让子类来实现的。

可以在线程执行前、后、终止状态执行自定义逻辑。

线程池隔离

线程池看似很美好,但也会带来一些问题。

如果我们很多业务都依赖于同一个线程池,当其中一个业务因为各种不可控的原因消耗了所有的线程,导致线程池全部占满。

这样其他的业务也就不能正常运转了,这对系统的打击是巨大的。

比如我们 Tomcat 接受请求的线程池,假设其中一些响应特别慢,线程资源得不到回收释放;线程池慢慢被占满,最坏的情况就是整个应用都不能提供服务。

所以我们需要将线程池进行隔离。

通常的做法是按照业务进行划分:

比如下单的任务用一个线程池,获取数据的任务用另一个线程池。这样即使其中一个出现问题把线程池耗尽,那也不会影响其他的任务运行。

 

hystrix 隔离

这样的需求 Hystrix 已经帮我们实现了。

Hystrix 是一款开源的容错插件,具有依赖隔离、系统容错降级等功能。

下面来看看 Hystrix 简单的应用:

首先需要定义两个线程池,分别用于执行订单、处理用户。

public class CommandOrder extends HystrixCommand<String> {

    private final static Logger LOGGER = LoggerFactory.getLogger(CommandOrder.class);

    private String orderName;

    public CommandOrder(String orderName) {


        super(Setter.withGroupKey(
                //服务分组
                HystrixCommandGroupKey.Factory.asKey("OrderGroup"))
                //线程分组
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("OrderPool"))

                //线程池配置
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                        .withCoreSize(10)
                        .withKeepAliveTimeMinutes(5)
                        .withMaxQueueSize(10)
                        .withQueueSizeRejectionThreshold(10000))

                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
        )
        ;
        this.orderName = orderName;
    }


    @Override
    public String run() throws Exception {

        LOGGER.info("orderName=[{}]", orderName);

        TimeUnit.MILLISECONDS.sleep(100);
        return "OrderName=" + orderName;
    }


}


/**
 * Function:用户服务
 *
 * @author crossoverJie
 *         Date: 2018/7/28 16:43
 * @since JDK 1.8
 */
public class CommandUser extends HystrixCommand<String> {

    private final static Logger LOGGER = LoggerFactory.getLogger(CommandUser.class);

    private String userName;

    public CommandUser(String userName) {


        super(Setter.withGroupKey(
                //服务分组
                HystrixCommandGroupKey.Factory.asKey("UserGroup"))
                //线程分组
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("UserPool"))

                //线程池配置
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                        .withCoreSize(10)
                        .withKeepAliveTimeMinutes(5)
                        .withMaxQueueSize(10)
                        .withQueueSizeRejectionThreshold(10000))

                //线程池隔离
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD))
        )
        ;
        this.userName = userName;
    }


    @Override
    public String run() throws Exception {

        LOGGER.info("userName=[{}]", userName);

        TimeUnit.MILLISECONDS.sleep(100);
        return "userName=" + userName;
    }


}

api 特别简洁易懂,具体详情请查看官方文档。

 

然后模拟运行:

 public static void main(String[] args) throws Exception {
        CommandOrder commandPhone = new CommandOrder("手机");
        CommandOrder command = new CommandOrder("电视");


        //阻塞方式执行
        String execute = commandPhone.execute();
        LOGGER.info("execute=[{}]", execute);

        //异步非阻塞方式
        Future queue = command.queue();
        String value = queue.get(200, TimeUnit.MILLISECONDS);
        LOGGER.info("value=[{}]", value);


        CommandUser commandUser = new CommandUser("张三");
        String name = commandUser.execute();
        LOGGER.info("name=[{}]", name);
    }

可以看到两个任务分成了两个线程池运行,他们之间互不干扰。

获取任务任务结果支持同步阻塞和异步非阻塞方式,可自行选择。

它的实现原理其实容易猜到:

利用一个 Map 来存放不同业务对应的线程池。

通过刚才的构造函数也能证明:

还要注意的一点是:

自定义的 Command 并不是一个单例,每次执行需要 new 一个实例,不然会报 This instance can only be executed once. Please instantiate a new instance. 异常。

数据预处理之数据平滑处理#matlab 介绍基于 MATLAB 的数据平滑方法,主要介绍 smooth 函数、smoothts 函数和 medfilt1 函数的用法。 阅读详情

相关推荐

Diffusion Model原理详解

Diffusion Model(扩散模型 )对标的是生成对抗网络(GAN),本文将用通俗的语言和公式为大家介绍Diffusion Model,并且结合公式为大家梳理Diffusion Model的代码。

weixin_42620109的博客 6098

Hystrix框架3--线程池

线程池 在Hystrix中Command默认是运行在一个单独的线程池中的,线程池的名称是根据设定的ThreadPoolKey定义的,如果没有设置那么会使用CommandGroupKey作为线程池。 这样每个Command都可以拥有自己的线程池而不会互相影响,同时线程池也可以很好地控制Command的并发量。 设置线程池配置 可以使用Setter来初始化Command在Setter中可以配置线程池的...

weixin_34270865的博客 358

增量式PID的P和I怎么理解(一)

增量式PID的P、I理解之推翻片 据我所知的资料里关于PID三个参数的理解是这样的:(我这里只说P和I,先不说D) P的作用:比例P可以加快系统调节速度,P过小调节时间变长,响应性不够,P越大响应性越高超调也越严重,过大的P会使系统振荡。 I的作用:消除静差,提高控制精度,I越大越滞后,过大会使系统振荡。 ...

fangchenglia的博客 1万+

微服务容错框架:Hystrix实现服务熔断、降级、限流

业务背景 在微服务架构体系下,服务间不可避免地会发生依赖关系,一般来说会通过REST Api来进行通信,这里先盗一个图来举例说明一个具体的业务场景(逃): 比如一个商城系统的微服务化结构,订单、商品、库存这三个服务是紧密依赖的,在理想情况下,什么问题都不发生当然是最好的。但服务运行期间难免会出现各种问题,如网络阻塞,延迟过高(比如因为内存泄露导致的Full GC次数飙高) ,甚至服务直接挂掉(比如流量激增把服务打挂了)等情况都是很有可能发生的。倘若库存服务挂掉了,那对于所有对库存服务有依赖关

august5291的博客 1240

经典烧脑算法逻辑题--高楼扔鸡蛋:最少次数测出鸡蛋摔碎临界点。

如果在第2层没碎,换到第3层扔.......如果第59层没碎,换到第60层扔;有2个鸡蛋,从100层楼上往下扔,以此来测试鸡蛋的硬度。比如鸡蛋在第9层没有摔碎,在第10层摔碎了,那么鸡蛋不会摔碎的临界点就是9层。因此,我们尝试每10层扔一次,第一次从10层扔,第二次从20层扔,第三次从30层......一直扔到100层。如果第一枚鸡蛋,在50层碎了,第二枚鸡蛋,就从第1层开始扔,一层一层增长,一直扔到第49层。如果第一枚鸡蛋在50层没碎了,则继续使用二分法,在剩余楼层的一半(75层)往下扔。

借雨醉东风的博客 1817

Qwen2.5-VL模型微调实战:从零到部署的完整流程(附避坑指南)

本文详细介绍了Qwen2.5-VL视觉语言模型从零开始的完整微调与部署流程。内容涵盖使用LLaMA-Factory进行LoRA微调的关键参数配置与避坑指南,以及如何将训练好的模型权重合并,并最终利用vLLM高性能推理引擎进行部署,为开发者提供了一套可落地的实战方案。

oo7890的博客 591

线程池研发学习笔记

可以直接叙述,也可以对比连接池介绍```线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象;```1:网购商品秒杀多人同时抢购某件商品,只有部分人会成功,其他人失败;2:云盘文件上传和下载同时可以上传多个文件或下载多个文件,且有最大上传和下载文件的个数限制;3:12306网上购票系统等。

liuhm的博客 2022

AWR1642+DCA1000EVM 不使用mmWave Studio软件采集原始ADC数据 Timeout问题汇总

DCA1000 EVM Timeout问题汇总

Jesy_Hsu的博客 1474

线程池详解

线程池

qq_52463861的博客 5282

Qt Quick/QML入门到精通_专栏demo对应文章目录(目前50个左右demo/长期更新)

自定义图标下拉列表框的效果如下所示:自定义三态按钮的效果如下所示:传送门:53.qt quick-自定义三态按钮、自定义图标下拉列表框_诺谦的博客-优快云博客传送门:59.qt quick-qml调色板(颜色取色器)实现、自定义QQuickStyle样式_诺谦的博客-优快云博客63.qt quick-QML侧边滑动栏(不需要任何图片资源,支持自定义左右方向和大小)_诺谦的博客-优快云博客_qt实现好看侧边栏。...

抵抗时间扭曲,坚持做一件有意义的事 2万+

6、直流微电网中恒功率负载(CPL)的稳定性分析与控制策略

本文探讨了直流微电网中恒功率负载(CPL)带来的稳定性问题,并深入分析了其影响机制及相应的缓解策略。文章首先通过小信号模型和非线性分析揭示了CPL对系统稳定性的影响,接着讨论了被动稳定方法的优缺点,最后重点介绍了基于时间的方法和几何控制器等控制策略。这些方法各有优劣,在实际应用中需结合具体需求选择合适方案,以实现直流微电网的稳定运行。

star的博客 227

线程池详解(建议收藏)

线程池(Thread Pool)是一种基于池化技术的多线程处理形式,用于管理线程的创建和生命周期,以及提供一个用于并行执行任务的线程队列。线程池的主要目的是减少在创建和销毁线程时所花费的开销和资源,提高程序性能,同时也提供了对并发执行任务的更好管理,例如控制线程数量。

wwwwwww31311的博客 1万+

硬方案——几种实用的恒流源电路设计方案,敬请收藏(运放、三极管在手,让你玩出多种花样)

0 引言 我们在信号测量、供电等应用场景时常会涉及到使用恒流源,以前觉得恒流源挺神奇的,研究了下实现方案,其实也没有多么高深(这里讨论的仅限于用在信号测量中的小电流恒流源设计),这里给出几个实用的恒流源电路设计方案。 1 恒流源电路设计 (1)使用三极管搭建的恒流源 (2)使用运放搭建的恒流源 (3)使用恒流二极管搭建的恒流源 pt100恒流源测温电路图_电子技术_电工之家 ...

数字世界 万物互联 9524

面试必问的线程池,你懂了吗?

面试必问的线程池,你懂了吗?

程序员囧辉 5万+

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

SpringBoot线程池开发最佳实践!

点击下方“JavaEdge”,选择“设为星标”第一时间关注技术干货!免责声明~任何文章不要过度深思!万事万物都经不起审视,因为世上没有同样的成长环境,也没有同样的认知水平,更「没有适用于所有人的解决方案」;别急着评判文章列出的观点,只需代入其中,适度审视一番自己即可,能「跳脱出来从外人的角度看看现在的自己处在什么样的阶段」才不为俗人。怎么想、怎么做,全在乎自己「不断实践中寻找适合自己的大道」0...

JavaEdge全是干货的技术号 1819

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Java线程池基础扫盲,不可多得的干货

分布式技术专题+面试解析+相关的手写和学习的笔记pdf还有更多Java笔记分享如下:助,可以添加V获取:vip1024b (备注Java)**[外链图片转存中…(img-iRdQs7KX-1711920434472)]分布式技术专题+面试解析+相关的手写和学习的笔记pdf还有更多Java笔记分享如下:[外链图片转存中…(img-zwggbduN-1711920434472)]

m0_61549674的博客 870

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

线程池(CompletableFuture)学习笔记(新手向纯干货)

线程池及CompletableFuture相关。

试问道几许 891

【041】CompletableFuture 实战封神!批量异步 + 线程池优化直接抄

通过王二使用CompletableFuture默认线程池导致系统崩溃的案例,揭示了高并发场景下的线程池陷阱。作者哇哥提出三大优化方案:线程池隔离、合理配置线程数、拒绝策略兜底,并给出改造后的实战代码模板,包含自定义线程池、超时控制和异常降级机制。该方案经过1000并发压测验证,能有效避免线程池雪崩问题,为开发者提供了CompletableFuture在生产环境中的最佳实践参考。

编程技术圈 1257

c++ socket线程池原理_写的很好!细数 Java 线程池的原理

点击上方IT牧场,选择置顶或者星标技术干货每日送达!来源:http://39sd.cn/077D6今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,最后讨论了一下如何合理配置线程池的大小。Java 中的 ThreadPoolExecutor 类java.uitl.concu...

weixin_39863008的博客 69

一把聊透线程池底层源码架构!!直接让你开发效率突飞猛进!

线程池底层源码分析:线程池通过Worker类管理线程执行任务,核心机制包括复用线程降低资源消耗、提升响应速度和提高可管理性。Worker继承AQS实现独占锁,根据任务量动态调整线程数,当任务队列满时创建新Worker,超过最大线程数则触发拒绝策略。Worker执行完任务后会阻塞监听队列,超时未获取任务则关闭,实现线程自动回收。深入理解线程池底层架构有助于优化并发性能,避免常见坑点。

likang_1167的博客 725

线程池实现原理_线程池的优点及其原理,代码实现线程池。简单、明了

一 使用线程池的好处池化技术应用:线程池、数据库连接池、http连接池等等。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。线程池提供了一种限制、管理资源的策略。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。使用线程池的好处:降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。提高线程的...

weixin_39854288的博客 75

线程池实现原理_线程池的优点及其原理,代码实现线程池。简单、明了。

一 使用线程池的好处池化技术应用:线程池、数据库连接池、http连接池等等。池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。线程池提供了一种限制、管理资源的策略。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。使用线程池的好处:降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。提高线程的...

weixin_39650091的博客 77

Java并行计算线程池_干货!线程池+CountDownLatch,实现 多线程并发计算、汇总

目录结构 抽象类:求和器 单线程 求和器 VS 多线程 求和器 1)线程池多个线程 一起并发执行,性能很生猛2)CountDownLatch主线程 使用 latch.await() 阻塞住,直到所有 子任务 都执行完毕了,才会继续向下执行。这样就保证了 逻辑的正确性所有 子任务 共享同一个 CountDownLatch 变量,实现 协同合作所有 子任务 的 finally块 中,必须要 latch...

weixin_39606137的博客 349

Java线程池的回收360°无死角分析,面试官再问线程池你就这样回答完美拿下offer!

最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行任务的流程有了大体了解,实际上这个流程也十分通俗易懂,就不再赘述了,别人写的比我好多了。 不过,我倒是对线程池是如何回收工作线程比较感兴趣,所以简单分析了一下,加深对线程池的理解吧。 下面以JDK1.8为例进行分析 1. runWorker(Worker w) 工作线程启动后,就进入runWorker(Worker w)方法。 里面是一个while循环,循环判断任务是否为空,若不为空,执行任务;若取不到任务,或发生异常,退出循环,

weixin_50917449的博客 3883

c++ 循环创建线程_干货:面试再被问到线程池底层原理,“狠狠”喷就是了!...

这篇文章,我们从源码级别来聊聊线程池的运行原理,帮大家彻底吃透线程池的底层!ThreadPoolExecutor在深入源码之前先来看看J.U.C包中的线程池类图:它们的最顶层是一个Executor接口,它只有一个方法:public interface Executor { void execute(Runnable command);}它提供了一个运行新任务的简单方法,Java线程池也称之为Exe...

weixin_42510768的博客 619

线程池中的 工作线程如何被回收

点击“终码一生”,关注,置顶公众号每日技术干货,第一时间送达!JDK中的ThreadPoolExecutor线程池相信大家都很熟悉,对于线程池的一些高频面试题,比如有哪些参数,每个参数的含义,什么时候发挥作用,工作流程等问题都能回答上来。而对于一些不是很常见的线程池面试题就显得有点模糊,比如:线程池中线程执行完了一个任务接下来是做什么,是等待还是被收回,如果是等待,那么判断的依据是啥,如果是被回收,那么是怎么被回收的。对于这些问题我们就必须深挖ThreadPoolExecutor源码知识了,而不是背几个常见

终码一生 892
上一篇: nginx教程(六)--日志管理配置
下一篇: 多线程的几种实现方式
Mr_baci
博客等级 码龄9年 1万+粉丝 68原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值