当前位置 : 主页 > 编程语言 > java >

为什么要设计非公平锁?

来源:互联网 收集:自由互联 发布时间:2023-02-04
背景 公平:排队 非公平:在合适时机插队 非公平还是 ReentrantLock 的默认策略,排队时间不浪费了? 场景 来看这种场景 假如A持有一把锁,B请求这把锁,这时候B被挂起进入阻塞,A释放

背景

公平:排队

非公平:在合适时机插队

非公平还是 ReentrantLock 的默认策略,排队时间不浪费了?

场景

来看这种场景

假如A持有一把锁,B请求这把锁,这时候B被挂起进入阻塞,A释放锁的时候,C来了进行请求,A就把锁给了C,因为唤醒B是需要很大开销的,很可能在B唤醒之前C就已经拿到这把锁执行完任务释放了这把锁,那就是双赢,C的执行速度相比于B被唤醒是很快的,这样设计就提高了整体运行效率

那假如公平呢?

那就进入等待队列等待,依次获得锁

不公平呢?

为什么要设计非公平锁?_System

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释放锁又获得了锁

优缺点

为什么要设计非公平锁?_公平锁_02

公平锁、非公平锁源码分析

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的线程就能获取到锁,即便设置的是公平锁模式,即便他前面有等待的线程,它也可以插队

网友评论