信号量(Semaphore)是对锁的扩展,内部锁synchronized和重入锁ReentrantLock一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程同时访问某个资源。
// 指定准入数,即能同时申请多少个许可
public Semaphore(int permits)
// fair指定是否公平
public Semaphore(int permits, boolean fair)
主要逻辑方法
// 尝试获取一个准入许可,若无法获得,则线程等待,直到有线程释放一个许可或者当前线程被中断
public void acquire();
// 不响应中断
public void acquireUninterruptibly();
//尝试获取,不会进行等待,立即返回
public boolean tryAcquire();
public boolean tryAquire(long timeout, TimeUnit unit);
// 线程访问资源结束后,释放一个许可
public void release();
示例代码
final Semaphore semap = new Semaphore(6);
public void run() {
try{
//申请一个信号量
semap.acquire();
doWork();
semap.release();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
CountDownLatch用来控制线程的等待,它可以让某个线程等待直到倒计时结束,再开始执行。
应用场景:比如流水线,后面的工序必须等到前面的工序做完之后才能继续做。
构造方法如下:
// 指定计时器数量
public CountDownLatch(int count);
示例代码
static final CountDownLatch latch = new CountDownLatch(5);
//子线程
public void run() {
try {
doWork();
latch.countDown(); //表示一个线程已经完成了一个流程任务,计时器减1
} catch(InterruptedException e) {
e.printStackTrace();
}
}
//主线程
public static void main(String[] args) {
ExecutorService exec = Excutors.newFixedThreadPool(5);
for(int i=0; i<5; i++) {
exec.submit(子线程);
}
//等待检查直到 计数为0
latch.await();
exec.shutdown();
}
CyclicBarrier是CountDownLatch的升级版,功能更复杂强大
Cyclic意为循环,就是说可以反复使用。比如指定计数器为10,当凑齐第一批10个线程后,计数器自动归0;接着下一批…
应用场景:10个人一组一组的完成某个任务。
构造方法如下:
// parties为计数总数,barrierAction 为一次计数完成后的回调
public CyclicBarrier(int parties, Runnable barrierAction);
示例代码
//子线程
public void run() {
try {
//等待所有线程到齐(达到计数),到齐后会第一次回调 barrierAction
cyclic.await();
doWork();
//进行下一轮计数
//等待所有线程 doWork 都做完,做完后会第二次回调 barrierAction
cyclic.await();
} catch(InterruptedException e) {
e.printStackTrace();
} catch(BrokenBarrierException e) {
e.printStackTrace();
}
}
对于BrokenBarrierException 异常,表示当前的栅栏CyclicBarrier已经破损了,可能系统已经没有办法等到所有的线程到齐了。如果继续等待,可能就是白等。需进行异常处理。
LockSupport 是一个阻塞工具,他可以在线程的任意位置阻塞线程。和Object.wait()相比,它不需要先获得某个对象的锁,不会抛出InterruptedException.
//阻塞当前线程
public static void park() {
UNSAFE.park(false, 0L);
}
//为当前线程设置阻塞对象,阻塞对象会出现在线程Dump中
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
//定时阻塞
public static void parkNanos(long nanos) {
if (nanos > 0)
UNSAFE.park(false, nanos);
}
public static void parkUntil(long deadline) {
UNSAFE.park(true, deadline);
}
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
1、定时中断
2、为当前线程设置一个阻塞对象
3、处于park()挂起线程的状态是WAITING状态,使用suspend()挂起线程的状态是Runnable状态。
4、LockSuport.park() 支持中断影响,但是不会抛出InterruptedException,只能通过Thread.interrupted() 等方法获取中断标记。
public void run() {
LockSupport.park();
if(Thread.interrupted()) {
System.out.println("线程被中断了");
}
System.out.println("执行结束了");
}