1。什么是线程池?随时准备执行任务。目标已经很明确了。创建一个池,其中存储约定数量的线程。这就是线程池,一种池化技术。如果线程数太少,则无法充分利用CPU。如果线程太多,上下文切换的消耗得不偿失,所以我们需要评估系统必须承载的并发量和要执行的任务的特性,得到大概需要的线程数充分利用CPU。因此,需要控制线程数。多线程技术主要解决处理器单元中多线程的问题。执行问题,它可以显着减少处理器单元的空闲时间,提高处理器单元的吞吐量假设一个服务器完成一个任务所需的时间为:T1线程创建时间,T2线程在线程中执行时间,T3销毁如果线程时间:T1+T3远大于T2,可以使用线程池来提高服务器性能。线程池技术的重点是如何缩短或调整T1和T3的时间,从而提高服务器程序的性能。它将T1和T3安排在服务端程序的起止时间段或一些空闲时间段,这样服务端程序在处理客户端请求时,不会有T1和T3的开销。线程池不仅调整了T1和T3生成的时间周期,而且还大幅度减少了创建线程数量的竞争,并且可能会占用过多的系统资源导致crash或者oom缺少更多的功能,比如定时执行,周期执行,线程中断为什么要使用线程池来减少创建和销毁线程的数量,并且每个工作线程都可以重复使用,可以执行多个任务。可以根据系统的容量调整线程池的工作线程数,防止服务器因为内存消耗过多而被耗尽(每个线程大约需要1MB内存,开的线程越多消耗的内存越大,最后崩溃)。ThreadPool的优点是减少了创建和销毁的线程数。每个工作线程都可以重复使用,并且可以执行多个任务。线程池中工作线程的数量可以根据系统的容量进行调整,防止线程消耗过多。内存,服务器耗尽(每个线程大约需要1MB内存,线程开的越多,消耗的内存越多,最后崩溃)减少创建和销毁线程的时间和系统资源的开销如果没有使用线程池,可能会导致系统创建大量线程,消耗系统内存。统一场景,对内存压力不敏感,但对系统负载敏感。第二种场景是:缓存线程池,其特点是线程数量不限,适用于需要低延迟的短期任务场景。第三种场景是:单线程线程池,它是一个线程的固定线程池,适用于需要异步执行但需要保证任务顺序的场景。第四种是:Scheduledthreadpool,适用于任务的周期性执行,支持固定频率、固定延迟的周期性执行。有两种定期执行的方法。第五个方法是:workstealing线程池,使用ForkJoinPool,这是一个固定并行度的多任务队列,适用于任务执行时间不均匀的场景。4.你听说过执行者吗?Java中线程池的顶层接口是Executor,但是严格来说,Executor并不是线程池,只是一个执行线程的工具。真正的线程池接口是ExecutorServiceExecutors是一个工具类,它提供了一些静态工厂来生成一些常用的线程池。Executors提供了四个线程池。newCachedThreadPool创建一个可缓存的线程池。如果线程池长度超过处理需要,可以灵活回收空闲线程。如果没有回收,则创建一个新线程newFixedThreadPool,创建一个定长线程池,可以控制最大并发线程数。超出的线程将在队列中等待。newScheduledThreadPool创建一个定长线程池,支持定时和周期性执行任务优先事项)。一般不会使用Executors提供的线程创建方法,使用ThreadPoolExecutor来创建线程池5.然后说说为什么阿里巴巴不推荐使用Executors静态工厂来构建线程池。阿里巴巴Java开发手册中提到,使用Executors创建线程池可能会导致OOM(OutOfMemory,内存溢出),真正导致OOM的是LinkedBlockingQueue.offer方法的底层,是通过LinkedBlockingQueue实现的。LinkedBlockingQueue是一个用链表实现的有界阻塞队列。容量可以任意设置。如果不设置,它将是一个最大长度为Integer的无界阻塞队列。MAX_VALUE的问题在于,如果不设置,它将是一个最大长度为Integer.MAX_VALUE的无界阻塞队列。也就是说,如果我们不设置LinkedBlockingQueue的容量,它的默认容量就是Integer.MAX_VALUE。对于无界队列,可以不断地向队列中添加任务。这种情况下,可能是由于task太多导致内存溢出问题避免使用Executors创建线程池,主要是避免使用默认实现,那么我们可以直接调用ThreadPoolExecutor的构造函数自己创建线程池。创建的同时,指定BlockQueue的容量即可6.线程池的核心参数是什么?第一个参数:设置核心线程数。默认情况下,核心线程会一直存活第二个参数:设置最大线程数。确定线程池最多可以创建多少个线程。第三个参数和第四个参数:用来设置线程的空闲时间,以及空闲时间的单位。当线程空闲时间超过空闲时间,就会被销毁。您可以使用AllowCoreThreadTimeOut方法来允许回收核心线程。第五个参数:设置缓冲队列。图中左下方的三个队列是设置线程池时经常用到的缓冲队列。ArrayBlockingQueue是一个有界队列,也就是说队列有Maximumcapacity的限制。LinkedBlockingQueue是一个无界队列,即队列没有容量限制。最后一个是SynchronousQueue,这是一个内部没有缓冲区的同步队列。第六个参数:设置线程池工厂方法,线程工厂用于创建新的线程,可以用来自定义线程的一些属性,比如线程组,线程名,优先级等,一般默认工厂类可以使用。第七个参数:设置线程池满时的拒绝策略。ThreadPoolExecutor默认有四种拒绝策略:ThreadPoolExecutor.AbortPolicy()直接抛出异常RejectedExecutionException,这是默认的拒绝策略ThreadPoolExecutor.CallerRunsPolicy()提交失败时,提交任务的线程直接执行提交的任务。ThreadPoolExecutor.DiscardPolicy()直接丢弃后续任务。ThreadPoolExecutor.DiscardOldestPolicy()丢弃队列中最早提交的任务。7、线程池的工作原理我们提交给线程任务可以使用Execute和Submit。区别在于Submit可以返回一个Future对象。通过Future对象,可以了解任务的执行情况,取消任务的执行,获取执行结果或执行异常。Submit最终是通过Execute执行的线程池执行的。提交任务时的执行顺序如下:向线程池提交任务时,会先判断线程池中的线程数是否大于设定的核心线程数。如果没有,则创建一个核心线程,如果大于核心线程数,则判断缓冲队列是否已满。如果未满,则放入队列,等待线程空闲时执行任务。如果队列已满,则判断是否达到线程池设置的最大线程数。如果未达到该数量,则创建一个新线程来执行该任务。如果已达到最大线程数,则执行指定的拒绝策略。这里需要注意队列的判断顺序和最大线程数的判断。不要误会。如果提交任务时线程池队列满了会怎样?如果你使用LinkedBlockingQueue,它是一个无界队列,没关系,继续往阻塞队列中添加任务等待执行,因为LinkedBlockingQueue可以认为是一个无限队列,可以无限存放任务。如果使用ArrayBlockingQueue等有界队列,任务会先添加到ArrayBlockingQueue,ArrayBlockingQueue已满。拒绝策略RejectedExecutionHandler将用于处理完整任务。默认值为AbortPolicy8。高并发、任务执行时间短的业务如何使用线程池?并发量低,任务执行时间长的业务如何使用线程池?高并发和业务执行长时间业务如何使用线程池?对于高并发和短任务执行时间,可以将线程池??的线程数设置为CPU核数+1,以减少线程上下文的切换。并发度低,任务执行时间长的业务需要考虑我们讨论一下,如果业务时间长,集中在IO操作上,也就是IO密集型任务,因为IO操作不占用CPU,所以不要让所有CPU闲置,可以增加线程池的线程数,让CPU去处理更多的业务,如果业务时间长,集中在计算操作上,也就是计算密集型的任务,就没办法了去做这个。线程数设置为CPU核心数+1,线程池中的线程数设置得更少。减少线程上下文切换并发度高,业务执行时间长。解决这类任务的关键不在于线程池,而在于整体架构的设计。第一步是看这些业务中的一些数据是否可以缓存。添加服务器这是第二步。至于线程池的设置,参考上面的设置。最后可能还需要分析一下业务执行时间长的问题,看能不能用中间件来拆分和解耦任务。9.听你提到ThreadLocal了吗?看我的介绍。10、简单介绍一下阻塞队列。元素的线程,直到队列已满。支持阻塞清除方式:当队列为空时,获取元素的线程会等待队列成为阻塞队列的非空应用场景。阻塞队列常用于生产者和消费者的场景。生产者是将元素添加到队列的线程。消费者是从队列中获取元素的线程。简而言之,阻塞队列是由生产者存储元素和消费者获取元素的容器1和ArrayBlockingQueue数组结构组成的有界阻塞队列。在这种情况下,并不能保证线程会公平地访问队列,即如果队列已满,阻塞线程访问队列的顺序不能保证线程公平性(即先阻塞,先插入).2、LinkedBlockingQueue是由链表结构组成的有界阻塞队列。该队列按照先进先出的原则对元素进行排序。3.PriorityBlockingQueue支持优先级无界阻塞队列。4.DelayQueue支持无界阻塞队列用于延迟获取元素,即可以指定需要多长时间才能从队列中获取当前元素。5.SynchronousQueue是一个不存储元素的阻塞队列。每一次put都必须等待一次take操作,否则无法添加元素。并支持公平访问队列。6、LinkedTransferQueue是由链表结构组成的无界阻塞TransferQueue队列。与其他阻塞队列相比,多了tryTransfer和transfer方法。如果当前有消费者在等待接收元素(take或poll方法有时间限制),则transfer方法可以立即将生产者传入的元素传输给消费者。如果没有消费者等待接收元素,则将元素放在队列的尾节点,等到元素被消费者消费后再返回tryTransfer方法,测试生产者传入的元素是否可以直接传递给消费者,如果没有消费者在等待,则返回false。与上述方法不同的是,该方法不管消费者是否收到,都会立即返回。transfer方法必须等到消费者消费完才返回。11、LinkedBlockingDeque是一个双向阻塞队列,链表结构。好处是当多个线程加入队列时,竞争减少了一半。二维码关注。转载本文请联系Java盗贼公众号。
