同步类
作者:互联网
1.Semaphore (信号量)
Semaphore 是一种基于计数的信号量,用来控制同时访问的线程数。
它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore 可以用来构建一些对象池,资源池之类的,比如数据库连接池
实现互斥锁(计数器为 1) 我们也可以创建计数为 1 的 Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量, 表示两种互斥状态。
1 // 创建一个计数阈值为 5 的信号量对象 2 // 只能 5 个线程同时访问 3 Semaphore semp = new Semaphore(5); 4 try { // 申请许可 5 semp.acquire(); 6 try { 7 // 业务逻辑 8 } catch (Exception e) { 9 } finally { 10 // 释放许可 11 semp.release(); 12 } 13 } catch (InterruptedException e) { 14 }
Semaphore 中也有Sync内部类(继承AQS抽象类),semp.acquire()方法和semp.release()方法底层使用Sync类CAS修改其内部volatile共享变量state来获取许可。state可以设置大于1,也就是成为了共享锁。
Semaphore semp = new Semaphore(5);就是将state设置为5,每次一个线程获得信号量就-1,释放一个信号量就+1。
Semaphore 与 ReentrantLock比较
Semaphore 基本能完成 ReentrantLock 的所有工作,使用方法也与之类似,通过 acquire()与 release()方法来获得和释放临界资源。经实测,Semaphone.acquire()方法默认为可响应中断锁, 与 ReentrantLock.lockInterruptibly()作用效果一致,也就是说在等待临界资源的过程中可以被 Thread.interrupt()方法中断。 此外,Semaphore 也实现了可轮询的锁请求与定时锁的功能,除了方法名 tryAcquire 与 tryLock 不同,其使用方法与 ReentrantLock 几乎一致。Semaphore 也提供了公平与非公平锁的机制,也可在构造函数中进行设定。 Semaphore 的锁释放操作也由手动进行,因此与 ReentrantLock 一样,为避免线程因抛出异常而无法正常释放锁的情况发生,释放锁的操作也必须在 finally 代码块中完成。
CountDownLatch
CountDownLatch(线程计数器 )
作用:让一个线程等待其他几个线程执行特定操作(调用countDown方法)后才能继续执行
CountDownLatch 类位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。比如有 一个任务 A,它要等待其他 4 个任务执行完毕之后才能执行,此时就可以利用 CountDownLatch 来实现这种功能了。
CountDownLatch 调用await方法当前线程会被阻塞,等待latch调用countDown(),count为0(Sync中state为0),此线程就会被唤醒。
1 final CountDownLatch latch = new CountDownLatch(2); 2 new Thread(){public void run() { 3 System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); 4 Thread.sleep(3000); 5 System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); 6 //重点 7 latch.countDown(); 8 };}.start(); 9 new Thread(){ public void run() { 10 System.out.println("子线程"+Thread.currentThread().getName()+"正在执行"); 11 Thread.sleep(3000); 12 System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕"); 13 latch.countDown(); 14 };}.start(); 15 System.out.println("等待 2 个子线程执行完毕..."); 16 //重点 17 latch.await(); //主线程执行,等待两个子线程调用 latch.countDown(); 18 System.out.println("2 个子线程已经执行完毕"); 19 System.out.println("继续执行主线程"); 20 }
CyclicBarrier
回环栅栏-等待至 barrier 状态再全部同时执行
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态(一组线程都调用await)之后再全部同时后续执行。
叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier 可以被重用。我们暂且把这个状态就叫做 barrier,当调用 await()方法之后,线程就处于 barrier 了。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 个重载版本: 1. public int await():用来挂起当前线程,直至所有线程都到达 barrier 状态再同时执行后续任务; 2. public int await(long timeout, TimeUnit unit):让这些线程等待至一定的时间,如果还有线程没有到达 barrier 状态就直接让到达 barrier 的线程执行后续任务。
具体使用如下,另外 CyclicBarrier 是可以重用的。
1 public static void main(String[] args) { 2 int N = 4; 3 4 CyclicBarrier barrier = new CyclicBarrier(N); 5 6 for(int i=0;i<N;i++) 7 8 new Writer(barrier).start(); 9 10 } 11 static class Writer extends Thread{ 12 private CyclicBarrier cyclicBarrier; 13 public Writer(CyclicBarrier cyclicBarrier) { 14 this.cyclicBarrier = cyclicBarrier; 15 } 16 @Override 17 public void run() { 18 try { 19 Thread.sleep(5000); //以睡眠来模拟线程需要预定写入数据操作 20 System.out.println("线程"+Thread.currentThread().getName()+"写入数据完 21 毕,等待其他线程写入完毕"); 22 23 cyclicBarrier.await(); 24 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 }catch(BrokenBarrierException e){ 28 e.printStackTrace(); 29 } 30 System.out.println("所有线程写入完毕,继续处理其他任务,比如数据操作"); 31 } 32 }
标签:同步,barrier,Thread,Semaphore,System,线程,CountDownLatch 来源: https://www.cnblogs.com/SEU-ZCY/p/16461188.html