此文记录学习《Java并发编程(核心方法与框架)》时的记录
代码见github:https://github.com/Bosssheep/JavaConcurrent
Semaphore,这个单词翻译过来指:信号
在Java中此类主要用来限制线程并发的数量。
其常用方法如下:
方法 | 作用 |
---|---|
Semaphore(int permits) | 构造方法,permits表示同一时间最多允许多少个线程同时执行acquire() 和release() 之间的代码 |
Semaphore() | 无参构造。默认permits=1 |
acquire(int permits) | 调用一次,就使用permits个许可,如果许可个数不够,则等待 |
acquireUninterruptibly() | 使等待进方法的线程,不允许被中断,即Thread.interrupt()无效 |
release() | 释放许可 |
availablePermits() | 返回Semaphore对象中当前可用的许可,等于初始化的permits减去其他线程已获得的许可数 |
drainPermits() | 获取并返回所有可用的许可个数,并且将许可置0 |
getQueueLength() | 返回等待许可的线程个数 |
hasQueuedThread() | 判断有没有线程在等待这个许可 |
tryAcquire() | 尝试获取1个许可,获取不到则返回false |
tryAcquire(int permits) | 尝试获取多个许可,获取不到则返回false |
tryAcquire(long timeout,TimeUnit unit) | 在time内尝试获取许可,获取不到则返回false |
Semaphore最常应用的一个模型的就是:生产者-消费者模型
Latch,单词的意思使:门阀、门锁。所以当门锁没有打开时,N个线程不能继续向下运行,直到满足某条件时让线程以“组团”的方式一起执行任务。
类CountDownLatch提供的功能是:判断count计数不为0时,则当前线程呈wait等待状态,也就是在屏障处等待,当count计数为0时多个等待中的线程可以一起从wait中唤醒并且继续执行。
主要的方法有两个:await()
和countDown()
await() 判断计数是否为0,不为0则呈等待状态。其他线程可以调用 countDown() 将计数-1,当计数减为0时,等待的线程们继续运行。
类CyclicBarrier不仅有CountDownLatch的功能,还可以实现阶段性同步,也就是循环地实现线程一起执行的目标,而不是像CountDownLatch一样仅支持一次线程与同步阻塞的特性。
它允许一组线程相互等待,直到到达某个公共屏障点。
与CountDownLatch不同的是,CyclicBarrier使用加法操作。
CyclicBarrier具有屏障重置性,这也是保证循环特性的一个体现。
常用的几个方法:
方法 | 作用 |
---|---|
CyclicBarrier(int parties) | 构造方法,parities表示到达屏障的条件为parties个线程 |
CyclicBarrier(int parties,runnable()) | 构造方法,当经过第一道屏障时调用runnable方法 |
await() | 直到parties个线程都调用了await()方法才继续往下执行 |
getNumberWaiting() | 获得有几个线程已经达到屏障点 |
getParties() | 获取Parties个数 |
reset() | 重置屏障 |
isBroken() | 查询此屏障是否处于损坏状态 |
对于屏障损坏状态的补充:
如果有一个线程由于中断或者超时而提前离开了屏障点,其他所有在屏障点等待的线程也会抛出BrokenBarrierException或者InterruptedException异常,并且离开屏障点,此时就会进入Broken状态。
CyclicBarrier类有一些缺陷,如不可以动态添加parties计数,调用await()仅占用1个parties。而Phaser都能解决这些问题。
Phaser类具有CyclicBarrier的功能,此外还提供动态增减parites计数,通过若干个方法来控制多个线程之间同步运行的效果,还可以实现针对某一个线程取消同步运行的效果,而且支持在指定屏障处等待、在等待时支持中断或非中断等功能。功能更加强大。
以下是Phaser常见的方法:
方法 | 功能 |
---|---|
Phaser(int) | 指定parties个数 |
arriveAndAwaitAdvance() | 当前线程已经到达屏障,当计数不足时,呈阻塞状态,等条件满足后继续向下一个屏障执行 |
arriveAndDeregister() | 使当前线程退出比赛,并且使parties值-1 |
getPhase() | 获取已经到达第几个屏障 |
onAdvance() | 通过新的屏障时被调用 |
getRegisteredParties() | 获得注册的parites数量 |
register() | 动态添加一个parties值 |
bulkRegister() | 批量增加parties数量 |
getArrivedParties() | 获得已经被使用的parties值 |
getUnarrivedParites() | 获得未被使用的parties值 |
arrive() | 使arrived parties数+1,并且不在屏障处等待,直接向下继续执行,如果经过屏障,同样具有计数重置功能;计数不足使,其他线程依然等待 |
awaiAdvance(int phase) | 如果传入参数phase值和当前getPhase方法返回值一样,则在屏障处,否则继续向下执行;不可中断 |
awaitAdvanceInterruptibly(int) | 同上,可中断 |
forceTermination() | 使Phaser对象的屏障功能失效 |
isTermination() | 判断是否实现 |
https://www.cnblogs.com/fengsehng/p/6048610.html
下面整理了一下常见接口和类的关系。
(一)Executor
Executor是接口,与线程池有关的大部分类都是实现该接口。
(二)ExecutorService
ExecutorService是Executor的子接口,其唯一子实现类是AbstractExecutorService(抽象类)。
(三)ThreadPoolExecutor
ThreadPoolExecutor是AbstractExecutorService的子类,可以进行实例化。
构造函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
//BlockingQueue可以用:LinkedBlockingQueue,SynchronousQueue
参数的各种情况:
1、当 任务数 < corePoolSize
,马上创建线程运行这个任务,不放入扩展队列中
2、当 任务数 > corePoolSize && 使用LinkedBlockingQueue
, maximumPoolSize和keepAliveTime参数忽略,并把任务放入LinkedBlockingQueue中等待被执行
3、当 任务数 > corePoolSize && 任务数 <= maximumPoolSize && 使用SynchronousQueue
, maximumPoolSize和keepAliveTime参数有效。马上创建非核心线程执行那些超过corePoolSize的任务,并且任务完成后在keepAliveTime后将非核心线程清除
4、当 任务数 > corePoolSize && 任务数 > maximumPoolSize && 使用SynchronousQueue
, maximumPoolSize和keepAliveTime参数有效。当执行超过maximumPoolSize将会抛异常
拒绝策略:
线程池的资源全部被占用的时候,对新添加的Task有不同处理策略。
拒绝策略由defaultHandler参数指定。
A、AbortPolicy:抛出RejectedExecutionException
B、CallerRunsPolicy:使用调用线程池的Thread线程去处理被拒绝的任务
C、DiscardOldestPolicy:放弃等待队列中最旧的未处理任务。将被拒绝的任务添加进去
D:DiscardPolicy:直接丢弃任务
(四)Executors
Executors是工厂类,可通过该接口创建指定类型的线程池
内部实现都是new一个ThreadPoolExecutor ,通过不同参数控制创建不同类型的线程池
。
方法 | 线程池名称 | 介绍 | Core | Max | AliveTime | BlockingQueue |
---|---|---|---|---|---|---|
newCachaedThreadPool() | 无界线程池 | 进行线程池自动回收,池中存放线程个数为Integer.MAX_VALUE | 0 | Integer.MAX_VALUE | 60L | SynchronousQueue |
newFixedThreadPool(int nThreads) | 有界线程池 | 固定最大线程数,超过则放入队列中等待 | nThreads | nThreads | 0L | LinkedBlockingQueue |
newSingleThreadExecutor() | 单一线程池 | 以队列形式执行任务 | 1 | 1 | 0L | LinkedBlockingQueue |
默认情况下,Thread对象不具有返回值的功能,但是结合Future和Callable,可以让线程具有返回值的功能
示例代码如下:
import java.util.concurrent.*;
/**
* ExecutorService的submit(Callable)与Future的get方法配合使用
* Future.get()方法具有阻塞特性
* submit() VS execute():
* execute没有返回值,submit具有返回值。
* execute出现异常直接打印堆栈信息,submit可以捕获;通过线程池设置UncaughtExceptionHandler,执行execute也可以捕捉异常
*/
class MyCallable implements Callable{
private int age;
public MyCallable(int age){
super();
this.age = age;
}
@Override
public String call() throws Exception {
Thread.sleep(3000);
return "返回值 年龄是"+age;
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("打印信息");
}
}
public class Test {
public static void main(String[] args) {
try{
//创建一个自定义Callable类
MyCallable callable = new MyCallable(100);
//创建一个ThreadPoolExecutor
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,3,5L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
//1、将Callable作为参数传入,打开线程执行Callable重写的Call()方法,并且返回类型为Future
System.out.println("----1----");
Future future = executor.submit(callable);
//通过Future的get方法获取任务返回值,此方法阻塞
System.out.println(future.get());
//2、将Runnable传入submit()方法
System.out.println("----2----");
Future future2 = executor.submit(new MyRunnable());
//如果submit传入的是Runnable
System.out.println(future2.get() + ","+future.isDone());
//3、将Runnable和result参数传入submit,result可以作为执行结果的返回值,替代Future的get方法
System.out.println("----3----");
UserInfo userInfo = new UserInfo();
Future future3 = executor.submit(new SetUserInfoRunnable(userInfo),userInfo);
userInfo = future3.get();
System.out.println("结果:"+userInfo.getUsername()+","+userInfo.getPassword());
}catch (Exception e){
}
}
}
CompletionService可以以异步的方式一边产生新任务,一边处理已完成任务。
解决了Future的阻塞的缺点。
ScheduledExecutorService可以将定时任务和线程池功能结合起来,让任务以周期形式执行。
使用RecursiveAction、RecursiveTask分解任务,合并任务结果。
本文具体代码:
https://github.com/Bosssheep/JavaConcurrent/tree/master/out/production/JavaConcurrent