什么是锁?
说到锁,门闩,密码锁,指纹锁,虹膜识别锁等,在计算机世界中,单机线程没有锁的概念,当有了资源竞争,才有锁的贵爱安出现。表示自己短暂的持有。
计算机锁从最开始的悲观锁,然后发展到后来的乐观锁,偏向锁,分段锁等。
锁有两种特性:互斥性和不可见性。
JUC 中的锁
并发包的类族,Lock 是 JUC 包的顶层接口。实现逻辑并未用到 synchronized ,而是利用 volatile 的可见性。
Lock 的继承类图,ReentrantLock 对于 Lock 接口的实现主要依赖了 Sync, 而 Sync 继承了 AbstractQueuedSynchronizer(AQS), AQS 是实现同步的基础工具。
在 AQS 中定义了,定义了一个 volatile int state 变量作为共享资源,如果线程获取资源失败,则进入 同步的 FIFO 队列;如果成功获取资源就执行临界区代码。
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } }
AQS 的子类可以定义不同性质的方法。
- 比如可重入锁 ReentrantLock ,定义 State 为0 时,可以获取资源并设置1,如果已经获得资源,state 不断+1 ,释放资源 state-1 直到为0;
- CountDown 初始化定义资源总量 state=count ,CountDown() 不断将 state-1 ,所以 CountDownLatch 是一次性的,用完之后只能重建一个,如果要循环使用,推进使用 CyclicBarrier 。
- Semaphore 与 CountDownLatch 不同,定义了资源总量 state=permits. 当state >0 就可以获得锁,并将 state-1.当 state=0时只能等待其他线程释放锁。当释放锁时 state+1。当 Semaphore 的permits定义为1时,为互斥锁。permits>1 为共享锁。
CyclicBarrier 例子
每次 await 都会 count-1
int index = --count;
测试代码:
public class CyclicBarrierSample { /** * @param args */ public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(5, new Runnable() { @Override public void run() { System.out.println("Action.... go again!"); } }); for (int i = 0; i < 5; i++) { Thread t = new Thread(new CyclicWorker(barrier)); t.start(); } } } class CyclicWorker implements Runnable { private CyclicBarrier barrier; public CyclicWorker(CyclicBarrier barrier) { this.barrier = barrier; } /** * @see java.lang.Runnable#run() */ @Override public void run() { try { for (int i = 0; i < 3; i++) { System.out.println("Executed!"); barrier.await(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }
CountDownLatch 代码
public class LatchSample { /** * @param args * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException { CountDownLatch latch = new CountDownLatch(6); for(int i = 0; i < 5 ; i++){ Thread t = new Thread(new FirstBatchWorker(latch)); t.start(); } for(int i = 0 ; i < 5;i++){ Thread t = new Thread(new SecondBatchWorker(latch)); t.start(); } while(latch.getCount() != 1){ Thread.sleep(100L); } System.out.println(" wait gor first batch finish"); latch.countDown(); } } class FirstBatchWorker implements Runnable { private CountDownLatch latch; public FirstBatchWorker(CountDownLatch latch) { this.latch = latch; } /** * @see java.lang.Runnable#run() */ @Override public void run() { System.out.println("First batch executed!"); latch.countDown(); } } class SecondBatchWorker implements Runnable { private CountDownLatch latch; public SecondBatchWorker(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { latch.await(); System.out.println("Second batch Executed!"); } catch (InterruptedException e) { e.printStackTrace(); } } }