背景 公平:排队 非公平:在合适时机插队 非公平还是 ReentrantLock 的默认策略,排队时间不浪费了? 场景 来看这种场景 假如A持有一把锁,B请求这把锁,这时候B被挂起进入阻塞,A释放
背景
公平:排队
非公平:在合适时机插队
非公平还是 ReentrantLock 的默认策略,排队时间不浪费了?
场景
来看这种场景
假如A持有一把锁,B请求这把锁,这时候B被挂起进入阻塞,A释放锁的时候,C来了进行请求,A就把锁给了C,因为唤醒B是需要很大开销的,很可能在B唤醒之前C就已经拿到这把锁执行完任务释放了这把锁,那就是双赢,C的执行速度相比于B被唤醒是很快的,这样设计就提高了整体运行效率
那假如公平呢?
那就进入等待队列等待,依次获得锁
不公平呢?
T1释放锁时,T5没排队直接请求,非公平策略:从整体效率考虑,这把锁会给T5
实验
import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class FairAndUnfair { public static void main(String args[]) { PrintQueue printQueue = new PrintQueue(); Thread thread[] = new Thread[10]; for (int i = 0; i < 10; i++) { thread[i] = new Thread(new Job(printQueue), "Thread " + i); } for (int i = 0; i < 10; i++) { thread[i].start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }}class Job implements Runnable { private PrintQueue printQueue; public Job(PrintQueue printQueue) { this.printQueue = printQueue; } @Override public void run() { System.out.printf("%s: Going to print a job\n", Thread.currentThread().getName()); printQueue.printJob(new Object()); System.out.printf("%s: The document has been printed\n", Thread.currentThread().getName()); }}class PrintQueue { private final Lock queueLock = new ReentrantLock(false); public void printJob(Object document) { queueLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n", Thread.currentThread().getName(), (duration / 1000)); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { queueLock.unlock(); } queueLock.lock(); try { Long duration = (long) (Math.random() * 10000); System.out.printf("%s: PrintQueue: Printing a Job during %d seconds\n", Thread.currentThread().getName(), (duration / 1000)); Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } finally { queueLock.unlock(); } }}false:不公平
这里只给出非公平锁结果
Thread 0: Going to print a jobThread 0: PrintQueue: Printing a Job during 8 secondsThread 1: Going to print a jobThread 2: Going to print a jobThread 3: Going to print a jobThread 4: Going to print a jobThread 5: Going to print a jobThread 6: Going to print a jobThread 7: Going to print a jobThread 8: Going to print a jobThread 9: Going to print a jobThread 0: PrintQueue: Printing a Job during 9 secondsThread 0: The document has been printedThread 1: PrintQueue: Printing a Job during 8 secondsThread 1: PrintQueue: Printing a Job during 2 secondsThread 1: The document has been printedThread 2: PrintQueue: Printing a Job during 7 secondsThread 2: PrintQueue: Printing a Job during 8 seconds...本应是1获得锁但此时被0插队,由此可见0释放锁又获得了锁
优缺点
公平锁、非公平锁源码分析
public class ReentrantLock implements Lock, java.io.Serializable { private final Sync sync; abstract static class Sync extends AbstractQueuedSynchronizer {...} static final class FairSync extends Sync {...} static final class NonfairSync extends Sync{...}}公平锁和非公平锁加锁方法源码
公平锁
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;}非公平锁
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false;}由此可见公平锁和非公平锁lock方法唯一区别就是公平锁获取锁时多了一个限制:hasQueuedPredecessors,等待队列中是否有线程在排队,公平锁:一旦有线程在排队,当前线程就不再尝试获取锁,对于非公平锁:无论有没有都尝试获取一下锁,获取不到再去排队
特例:tryLock方法
上源码
public boolean tryLock() { return sync.nonfairTryAcquire(1);}意思就是一旦有线程释放锁,那么正在tryLock的线程就能获取到锁,即便设置的是公平锁模式,即便他前面有等待的线程,它也可以插队