公平性锁与非公平性锁
非公平性: 如果一个线程因为CPU时间全部被其他的线程抢走而无法获得CPU的执行时间,这种状态称之为饥饿,而该线程被称为“饥饿致死”,非公平锁就存在“饥饿”,因为线程得不到CPU的运行时间机会。
公平性: 所有的线程均能公平性的获取到执行的机会。
线程饥饿的原因:
高优先级的线程抢夺所有低优先级的线程CPU时间;线程永远阻塞在进入同步块的状态;线程在等待一个本身也处于永久等待的完成的对象(例如该对象调用了wait方法)。
公平性锁:
Lock类转换成公平锁FairLock,每一个Lock调用的线程都回去进入到队列,当解锁后, 只有队列中的第一个线程被允许获取锁。
ReentrantLock是重入锁的一种实现,一次只能由一个线程持有锁实现了Lock接口。其中包含三个内部类:Sync,NonfairSync,FairSync。
public class ReentrantLock implements Lock, java.io.Serializable
abstract static class Sync extends AbstractQueuedSynchronizer
static final class NonfairSync extends Sync
static final class FairSync extends Sync
ReentrantLock的锁的实现委托于其内部类。
公平性锁和非公平性锁的父类Sync的实现:
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; //序列化 abstract void lock(); //抽象的加锁方法,需要子类来实现 final boolean nonfairTryAcquire(int acquires) { //非公平和公平性加锁操作的都需要调用的方法 final Thread current = Thread.currentThread(); int c = getState(); //获取AQS中state属性值,state = 0:锁空闲,大于0: 锁占用 小于0:锁溢出 if (c == 0) { //锁空闲 if (compareAndSetState(0, acquires)) { //通过CAS来确保多线程的安全性 setExclusiveOwnerThread(current); //设置当前持有锁的线程 return true; //加锁成功 } } //锁占用(当前线程持有锁或者其他线程持有锁)或者锁溢出 else if (current == getExclusiveOwnerThread()) { //判断是否当前线程获取锁(可重入锁的体现,当前线程可以再次获取到锁,并且对state修改) int nextc = c + acquires; //对持有锁的次数进行变更 if (nextc < 0) //被锁次数上溢(超过int的最大值), throw new Error("Maximum lock count exceeded"); setState(nextc); //当前持有锁的线程变更锁保护的资源 return true; //获取锁成功 } return false; } /** * 释放锁调用的方法 */ protected final boolean tryRelease(int releases) { int c = getState() - releases; //只能由当前持有锁的线程释放锁 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; //锁被释放 setExclusiveOwnerThread(null); //将持有锁的线程信息置为null } setState(c); //变更锁的状态 return free; //释放锁操作,当 c==0时,才能真正释放锁,不等于0时,只能变更锁状态,不能真正释放 } //释放当前线程持有的锁 protected final boolean isHeldExclusively() { return getExclusiveOwnerThread() == Thread.currentThread(); } //获取ConditionObject对象 final ConditionObject newCondition() { return new ConditionObject(); } //获取持有锁的线程 final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } //获取加锁次数 final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } //判断是否加锁 final boolean isLocked() { return getState() != 0; } }
公平性锁的实现(FairSync):
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } //公平锁实现的tryAcquire 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"); //变更state值 setState(nextc); return true; } return false; } } AQS类中方法: public final void acquire(int arg) { if (!tryAcquire(arg) && //获取锁失败时,当前线程加入到等待队列 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } //当前线程是否处于对队列第一个或者是当前队列为null public final boolean hasQueuedPredecessors() { Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
非公平性锁的实现(NonFairSync)
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; //lock加锁操作 final void lock() { if (compareAndSetState(0, 1)) //直接通过CAS抢锁,true:抢锁成功 setExclusiveOwnerThread(Thread.currentThread());//设置锁的持有者 else acquire(1); //获取锁失败,进入到常规流程,acquire会首先调用tryAcquire } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } AQS类中的: public final void acquire(int arg) { if (!tryAcquire(arg) && //获取锁失败false(取反也就是true),继续进行后续的判断 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // selfInterrupt(); }
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode); //现在要获取锁的线程
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
两个构造函数为:
public ReentrantLock() { sync = new NonfairSync();//默认为非公平性锁 } public ReentrantLock(boolean fair) { //fair决定公平性锁和非公平性锁(true -->为公平性锁 false -->非公平性锁 ) sync = fair ? new FairSync() : new NonfairSync(); }
方法:
public void lock() { //加锁操作 sync.lock(); } public boolean tryLock() { //尝试下获取锁,非公平性锁 return sync.nonfairTryAcquire(1); } public void unlock() { //释放锁 sync.release(1); } public Condition newCondition() { //用来创建线程间通讯对象的方法 return sync.newCondition(); } public final boolean hasQueuedThreads() { //判断是否有在等待队列的线程,等待获取锁 return sync.hasQueuedThreads(); }
对于公平性锁和非公平性锁实现的加锁操作:
代码试下:
public class Test implements Runnable{ private ReentrantLock lock; //锁实例 private static Integer num = 0; public Test(ReentrantLock lock) { this.lock = lock; } @Override public void run() { while (true){ lock.lock(); num++; System.out.println(Thread.currentThread().getName()+": "+num); lock.unlock(); } } public static void main(String[] args) { //公平性锁的实现 ReentrantLock fairLock = new ReentrantLock(true); new Thread(new Test(fairLock),"线程A").start(); new Thread(new Test(fairLock),"线程B").start(); //非公平性锁的实现 ReentrantLock NofairLock = new ReentrantLock(false); new Thread(new Test(NofairLock),"线程A").start(); new Thread(new Test(NofairLock),"线程B").start(); } }
1.
公平性锁运行结果(A,B线程交替执行):
2.
非公平性锁运行结果:
注意:使用多把锁时,遵循先加锁后释放,后加锁先释放的原则。