前言
像我大佬烟说过:面试的时候颜值可以提高分数,没有颜值的话就需要会点J.U.C。人丑就要多读书,没错说的就是我。 AQS是AbstractQueuedSynchronizer缩写,作者是
@author Doug Lea
并发大神,致敬一哈。J.U.C很多东西都是基于AQS进行实现的,所以有必要去研究它。
AQS
重要变量
state加锁的个数 Node 类似链表的实现,可以储存Thread的顺序 cas的引用
private static final Unsafe unsafe = Unsafe.getUnsafe(); Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。不过尽管如此,JVM还是开了一个后门,JDK中有一个类Unsafe,它提供了硬件级别的原子操作
乐观锁也是cas思想的一种实现,这里是使用cpu 底层来实现cas。 cas引入目的 在无锁的情况下,线程安全的执行赋值操作 像state(持有锁的个数)这个需要控制原子性的,其次是可见性:volatile
/** * The synchronization state. */ private volatile int state;
unsafe
上面已经解释了这个类,可以调用硬件级别的原子操作。
unsafe.compareAndSwapInt(this, stateOffset, expect, update);
cas就不扯了,西西~
AtomicInteger
/** * Atomically adds the given value to the current value. * * @param delta the value to add * @return the updated value */ public final int addAndGet(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta) + delta; }
可以看到也是使用cas去进行原子性增长
ReentrantLock
可重入锁,就是你原本租的单车,还没还,再次去租这一辆,还是你的。只是会标记你借了两次。
lock方法
ReentrantLock有公平锁,非公平锁
都会调用acquire方法
tryAcquire是一个重要方法,后面解读
加锁原理
判断AQS的state,如果为0,加锁并记录当前线程。如果state不为0,判断当前线程是否是当前lock保存的线程,是的话state+1。否的话会调用addWaiter,将线程放到AQS Node 插入到队列里面等待。
/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
公平锁
每个线程都有鸡费拿到锁
非公平锁
ReentrantLock默认是非公平锁。 看运气拿到锁。
公平锁跟非公平锁的代码区别
FairSync hasQueuedPredecessors
公平锁会从AQS Node队列里面拿,采用FIFO先进先出的算法,大家都有鸡费拿到锁。
非公平锁是没有从AQS去拿排队的线程,大家随缘抢锁。
共享锁系列
何为共享锁?大家都可以去获取,而不是只能一个线程去获取。就像饭堂一样,大噶都可以去打饭,独占锁:大家排成一队,老子占了位置,没有点完餐,其他人点不了。
Semaphore
信号量控制,限流专用,西西~
ReentrantLock是独占锁,Semaphore是共享锁
源码
包含公平锁和非公平锁,之前提及过,这里就不再解释~主要是讲下共享锁和独占锁的区别! 共享锁和独占锁的区别
独占锁
解读:只能当前线程去获取,如果是公平锁,而且当前锁不属于它的,排队去。非公平锁,则你抢到算你的。
共享锁
解读:大家随便抢,饭堂记录你们领了几份盒饭,盒饭不够的时候你们等等,等到有盒饭再分发。
方法名也有所不同:nonfairTryAcquire & nonfairTryAcquireShared ,有点东西~
至于semaphore控制并发的其他原理需要看下它的acquire方法
/** * Acquires in shared interruptible mode. * @param arg the acquire argument */ private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
CountDownLatch
这个大家也不陌生吧,就是一个起泡枪,啪......大家线程一起跑
await源码
countDown源码
public void countDown() { sync.releaseShared(1); }
CyclicBarrier
概念:大家自由活动,集合声一响大家就过来集合(线程各自跑,跑完一起结束)
源码
本身来看跟AQS没有太大关系,跟ReentrantLock以及Condition有关系,await来阻塞。用屁股也能想出来,大家线程随便跑,跑完阻塞你,等大家跑完再释放。