高并发与多线程网络学习笔记(九)并发包
Phaser 相位器
功能描述
它与CountDownLatch/cyclicBarrier类似,但是它的功能更加强大
- CyclicBarrier,允许一组线程互相等待,直到到达某个公共屏障点。它提供的await()可以实现让所有参与者在临界点到来之前一直处于等待状态。
- CountDownLatch,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。它提供了await()、countDown()两个方法来进行操作。
- 在Phaser中,它把多个线程协作执行的任务划分为多个阶段,编程时需要明确各个阶段的任务,每个阶段都可以有任意个参与者,线程都可以随时注册并参与到某个阶段。
要点描述
- 它主要可以动态注册,动态增加每个阶的等待点
- party如果不取消注册,每一个阶段都要等待party数量的线程到达
API说明
Phaser() Phaser(int parties) //可以自定义参与方个数也可以不定义
arriveAndAwaitAdvance() //到达此处等待其他人到达,unarrived减一
register() // party和unarrived加1,动态注册一个线程
arriveAndDeregister() //到达此移相器并从中取消注册而无需等待其他人到达。
awaitAdvance(int phase) //如果传入参数phase值和当前getPhase()方法返回值一样,则在屏障处等待,否则继续向下面运行,可以用于监控
arrive() //到达会使unarrive减一,但是不会阻塞等待其他线程
getRegisteredParties() //注册的参与方
getUnarrivedParties() //未到达的参与方
getArrivedParties() //到达的参与方
getPhase() //进行的阶段次数,相当于cyc循环了多少次
forceTermination() //强制中介,无法再使用该相位器
示例
public class PhaserTest {
public static final int PARTIES = 3;
public static final int PHASES = 4;
public static void main(String[] args) {
Phaser phaser = new Phaser(PARTIES) {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
// 【本篇文章由公众号“彤哥读源码”原创,请支持原创,谢谢!】
System.out.println("=======phase: " + phase + " finished=============");
return super.onAdvance(phase, registeredParties);
}
};
for (int i = 0; i < PARTIES; i++) {
new Thread(()->{
for (int j = 0; j < PHASES; j++) {
System.out.println(String.format("%s: phase: %d", Thread.currentThread().getName(), j));
phaser.arriveAndAwaitAdvance();
}
}, "Thread " + i).start();
}
}
}
Executor 线程池
ThreadPoolExecutor 七大参数
- corePoolSize
核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。
核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
- maxPoolSize
当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。
- keepAliveTime
当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
- TimeUnit unit
keepAliveTime的时间单位
- BlockingQueue<Runnable> workQueue
用来hold task的队列,当你使用submit的时候会进入队列。天然的生产者消费者实现,如果满了会block住,如果取了就notify
常用的有三种队列SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。
SynchronousQueue:是一个没有数据缓冲的阻塞队列,其中每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。因此此队列内部其 实没有任何一个元素,或者说容量是0,严格说并不是一种容器,使用SynchronousQueue阻塞队列一般要求maximumPoolSizes为无界,如果都在忙碌状态就会创建新的线程。
LinkedBlockingDeque:当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。(所以在使用此阻塞队列时maximumPoolSizes就相当于无效了),每个线程完全独立于其他线程。生产者和消费者使用独立的锁来控制数据的同步,即在高并发的情况下可以并行操作队列中的数据。
ArrayBlockingQueue:是一个有界缓存等待队列,可以指定缓存队列的大小,当正在执行的线程数等于corePoolSize时,多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会报错。(最符合正常人的队列逻辑)
- ThreadFactory threadFactory
通过什么方式构造线程,可以通过函数式接口传入构造线程的方法,可以对线程的一些属性,比如说守护线程进行定制
- RejectedExecutionHandler handler
拒绝策略,也是函数式接口
void rejectedExecution(Runnable var1, ThreadPoolExecutor var2);一下的方法都是继承该类
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最老的,执行新进的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
线程池关闭
线程池为什么需要关闭,因为线程工作完只是进入idle状态,没有销毁,只有销毁所有线程,线程池才能关闭,程序才能终结。
注意执行关闭,会对工作线程执行interrupt(),如果工作线程本身有lock,sleep方法可能比较危险,关闭后无法在提交任务。
注意可以通过threadFactory设置守护线程,强制终结
- shutdown()
它是非阻塞方法
停止接收外部submit的任务
尝试将正在跑的任务interrupt中断
内部正在跑的任务和队列里等待的任务,会执行完
等到第二步完成后,才真正停止
- shutdownNow()
它是非阻塞方法
停止接收外部submit的任务
忽略队列里等待的任务,并返回等待队列的任务列表
尝试将正在跑的任务interrupt中断
- awaitTermination (long timeOut, TimeUnit unit)
当前线程阻塞,直到
等所有已提交的任务(包括正在跑的和队列中等待的)执行完
或者等超时时间到
或者线程被中断,抛出InterruptedException
- 优雅的关闭,用shutdown()
- 想立马关闭,并得到未执行任务列表,用shutdownNow()
- 优雅的关闭,并允许关闭声明后新任务能提交,用awaitTermination()
Executors四种线程池
七个参数很麻烦,但是需要掌握,java提供了封装类隐藏了七个参数,专注于应用场景
newCachedThreadPool
(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,SynchronousQueue)
- 特点:每次遇到新任务都会创建新线程,适用于很多短暂存活的线程。
- 特点:有自动超时回收机制,可以不需要显式调用shutdown
newFixedThreadPool
(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
- 特点:控制池子容量,不会扩容创建线程,也不会进行回收销毁
- 特点:超出线程池容量,会进入等待队列,注意等待队列是不设上限知道outMemory。
newSingleThreadExecutor
1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
- 特点:只有一个线程,跟newFixedThreadPool(1)一样,不好用,比起单纯使用thread要好,因为有任务队列。
- 特点:它是不会被自动销毁的
newScheduledThreadPool
- 定时有专门的框架,一般很少用这个了
线程池方法
线程池的返回值都是ExecutorService
submit();与execute();区别
- submit可以传入runnable,callable;execute只能传入runnable;
- submit 返回一个future,可以在外部判断任务状态;execute没有返回值不可控制;
runnable与callable区别
- 实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回执行结果
- Callable接口实现类中run()方法允许将异常向上抛出,也可以直接在内部处理(try...catch); 而Runnable接口实现类中run()方法的异常必须在内部处理掉,不能向上抛出
invoke()和execute()
invoke对应的是callable,execute对应的是runnable
preStartCoreThread()
线程池一开始如果没有任务是不会创建线程,用这个方法可以提前创建
getActiveCount()
获取活跃线程数,它不包括空闲线程

浙公网安备 33010602011771号