java 多线程-线程池

先看一段熟悉的代码
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <!-- 线程池维护线程的最少数量 -->
        <property name="corePoolSize" value="10" />
        <!-- 允许的空闲时间 -->
        <property name="keepAliveSeconds" value="3000" />
        <!-- 线程池维护线程的最大数量 -->
        <property name="maxPoolSize" value="15" />
        <!-- 缓存队列 -->
      <property name="queueCapacity" value="1000" />
        <!-- 对拒绝task的处理策略 -->
        <property name="rejectedExecutionHandler">
         <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
       </property>
   </bean>

以上代码可能经常出现在我们的代码中,由此切入可能更好
先对以上容易误解的参数进行解释,可能大家都熟烂于心了

corePoolSize:为线程池核心线程数量
queueCapacity:为队列等待的线程数量
maxPoolSize:为当等待的线程数量达到最大值时,线程池中活跃的线程数量

Java给我们提供的线程池

java 提供 Executors 类中,有许多静态方法可以直接返回适应各种场景的线程池

1. Executors.newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

创建一个线程池,根据需要创建新线程,但是将重写之前线程池的构造。
这个线程池通常会提高性能去执行许多短期异步任务的程序。
如果有可用线程,当线程池调用execute, 将重用之前的构造函数。
如果没有现有的线程可用,那么就创建新的线程并添加到池中。
线程没有使用60秒的时间被终止并从线程池里移除缓存。

因此,一个闲置时间足够长的线程池不消耗任何资源。
注意,线程池有类似的属性,但有一些不同的细节(例如,超时参数)可以使用@link ThreadPoolExecutor构造函数创建。

2. Executors.newFixedThreadPool:

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

创建一个线程池,使用固定数量的线程在共享的无界队列中操作。
在任何时候,有最多  nThreads(就是我们传入参数的数量)的线程将处理任务。
如果所有线程都处于活动状态时,提交额外的任务,他们会在队列中等待,直到有一个线程可用。

如果在执行过程中出现故障,任何线程都会终止。如果需要执行后续任务,新的任务将取代它的位置。线程池中的线程会一直存在,直到它显式为止(调用shutdown)
nThreads 就是传入线程池的数量  ,当nThreads  <= 0 就会抛异常IllegalArgumentException

3. Executors.newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行。

创建一个线程池,它可以安排在 a 之后运行的命令给定延迟,或定期执行。
corePoolSize (这个参数) 是指在池中保留的线程数,即使它们是空闲的。这个函数最终会返回一个新创建的调度线程池
如果 corePoolSize < 0 ,则会抛出 IllegalArgumentException
Ps:这个还支持多传入一个ThreadFactory

4. Executors.newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

创建一个线程执行器,它使用单个运行中的线程操作在一个无界队列中。
请注意,如果这个单独的线程终止是因为在执行前异常或者终止,若需要执行后续的任务,那么就需要一个新的去替代它。
任务被保证按顺序的执行,并且在任何给定的时间内不超过一个任务将是活动的。
不像其他等价 newFixedThreadPool(1) 这个返回的线程池对象是保证不运行重新配置以使用额外的线程。
最终返回的是一个重新创建的单线程去执行。

以上方法都会返回一个ExecutorService,它继承自Executor,这是java给我们提供的

ExecutorService和ThreadPoolTaskExecutor **

ExecutorService由java提供
ThreadPoolTaskExecutor由spring提供
这里写图片描述
使用:
往ThreadPoolTaskExecutor中添加一个线程,需要调用execute(…),里面传入runnable对象
往ExecutorService中添加线程,需要调用submit(…) 方法,入参可以是Runnable或者是Callable,该方法会放回一个Future对象

ExecutorService的子类ThreadPoolExecutor

public abstract class AbstractExecutorService implements ExecutorService
public class ThreadPoolExecutor extends AbstractExecutorService
ThreadPoolExecutor构造

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue)
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory)      
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler)    
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)               
  • int corePoolSize (core:核心的) = > 该线程池中核心线程数最大值
    什么是核心线程:线程池新建线程的时候,如果当前线程总数小于 corePoolSize ,则新建的是核心线程;如果超过corePoolSize,则新建的是非核心线程。
    核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干(闲置状态)。
    如果指定ThreadPoolExecutor的 allowCoreThreadTimeOut 这个属性为true,那么核心线程如果不干活(闲置状态)的话,超过一定时间( keepAliveTime),就会被销毁掉

  • int maximumPoolSize = > 该线程池中线程总数的最大值
    线程总数计算公式 = 核心线程数 + 非核心线程数。

  • long keepAliveTime = > 该线程池中非核心线程闲置超时时长
    注意:一个非核心线程,如果不干活(闲置状态)的时长,超过这个参数所设定的时长,就会被销毁掉。但是,如果设置了 allowCoreThreadTimeOut = true,则会作用于核心线程。

  • TimeUnit unit = > (时间单位)
    首先,TimeUnit是一个枚举类型,翻译过来就是时间单位,我们最常用的时间单位包括:
    MILLISECONDS : 1毫秒 、SECONDS : 秒、MINUTES : 分、HOURS : 小时、DAYS : 天

  • BlockingQueue workQueue = >( Blocking:阻塞的,queue:队列)
    该线程池中的任务队列:维护着等待执行的Runnable对象。当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务

  • ThreadFactory threadFactory = > 创建线程的方式,这是一个接口,new它的时候需要实现他的Thread newThread(Runnable r)方法

  • RejectedExecutionHandler handler = > 这个主要是用来抛异常的
    当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。

BlockingQueue
这里写图片描述

  • offer(E e): 将给定的元素设置到队列中,如果设置成功返回true, 否则返回false. e的值不能为空,否则抛出空指针异常。

  • offer(E e, long timeout, TimeUnit unit): 将给定元素在给定的时间内设置到队列中,如果设置成功返回true, 否则返回false.

  • add(E e): 将给定元素设置到队列中,如果设置成功返回true, 否则抛出异常。如果是往限定了长度的队列中设置值,推荐使用offer()方法。

  • put(E e): 将元素设置到队列中,如果队列中没有多余的空间,该方法会一直阻塞,直到队列中有多余的空间。

  • take(): 从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。

  • poll(long timeout, TimeUnit unit): 在给定的时间里,从队列中获取值,如果没有取到会抛出异常。

  • remainingCapacity():获取队列中剩余的空间。

  • remove(Object o): 从队列中移除指定的值。

  • contains(Object o): 判断队列中是否拥有该值。

  • drainTo(Collection c): 将队列中值,全部移除,并发设置到给定的集合中。

常用的workQueue类型:

一般来说,workQueue有以下四种队列类型:

  • SynchronousQueue:(同步队列)这个队列接收到任务的时候,会直接提交给线程处理,而不保留它(名字定义为 同步队列)。但有一种情况,假设所有线程都在工作怎么办?

  • 这种情况下,SynchronousQueue就会新建一个线程来处理这个任务。所以为了保证不出现(线程数达到了maximumPoolSize而不能新建线程)的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大,去规避这个使用风险。

  • LinkedBlockingQueue(链表阻塞队列):这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

  • ArrayBlockingQueue(数组阻塞队列):可以限定队列的长度(既然是数组,那么就限定了大小),接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

  • DelayQueue(延迟队列):队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

Future
闭锁CountDownLatch

闭锁可以延迟线程直到到达终止状态。闭锁相当于一扇门:在闭锁到达终止状态之前一直时关闭的,没有任何线程能通过,到达终止后,这扇门永远保持打开状态 用来确保某些活动直到其他活动都完成后才继续执行

信号量Semaphore

计数信号量用来控制同时访问某个特定资源的操作数量,还可以实现某种资源池,或对某容器的边界保护
Semaphore 管理 一组虚拟的许可,许可数量由初始化指定,在执行操作时首先要获得许可(只要还有剩余的许可),并在操作后释放许可。它是不可重入锁,谁拥有唯一的许可,谁就拥有互斥锁
没有许可时,acquire()将阻塞,release()释放许可

 public class BoundedHashSet<T>{
         private final Set<T> set;
         private final Samaphore sem;
         public BoundedHashSet(int bound){
             this.set = Collectins.synchronizedSet(new HashSet<T>());
             sem = new Semaphore(bound);
         }
         public boolean add(T o) throw InterruptedException{
             sem.acquire();
             boolean wasAdded = false;
             try{
                 wasAdded = set.add(o);
                 return wasAddes;
             }finally{
                 if(!wasAdded)
                    sem.release();
             }
         }
         public boolean remove(Object o){
             boolean wasRemoved = set.remove(o);
             if(wasRemoved){
                 sem.release();
             }
             return wasRemoved ;
         }
     }

你也可以使用Semaphore将任何一种容器变成有界阻塞容器

栅栏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值