Java锁(synchronized、AQS、Lock、ReentrantLock、公平锁、非公平锁)

    科技2025-09-23  70

    Java中的锁

    synchronized实现原理1.6版本做了什么优化对象头锁升级机制偏向锁轻量级锁 Java语言实现的锁LockSupport 类AQSLock接口ReentrantLock源码分析 Demo1Demo2Demo3

    synchronized

    synchronized用于并发场景,在1.6版本之前被称为重量级锁,也是一种悲观锁,可重入锁。1.6版本进行可优化,大大提高了synchronized的效率。

    注意,synchronized关键字修饰类和静态方法,锁住类对象;修饰方法,锁住调用此方法的对象

    实现原理

    synchronized是jvm实现的一种互斥同步访问方式,底层是基于每个对象的监视器(monitor)来实现的。被synchronized修饰的代码,在被编译器编译后在被修饰的代码前后加上了一组字节指令。

    在代码开始加入了monitorenter,在代码后面加入了monitorexit,这两个字节码指令配合完成了synchronized关键字修饰代码的互斥访问。

    在虚拟机执行到monitorenter指令的时候,会请求获取对象的monitor锁,基于monitor锁又衍生出一个锁计数器的概念。

    当执行monitorenter时,若对象未被锁定时,或者当前线程已经拥有了此对象的monitor锁,则锁计数器+1,该线程获取该对象锁。

    当执行monitorexit时,锁计数器-1,当计数器为0时,此对象锁就被释放了。那么其他阻塞的线程则可以请求获取该monitor锁。

    1.6版本做了什么优化

    Java 1.6版本为了减少获得锁和释放锁带来的性能消耗,引入了偏向锁和轻量级锁的概念,级别从高到低分别是重量级锁、轻量级锁、偏向锁。只能升级不能降级。

    对象头

    对象头三部分组成,Mark Word,Class Pointer, Array Length 关于锁的信息就存储在对象头里 原图来自《并发编程的艺术》这本书。 可以看到Markword 内有2比特是锁标志位,还有一位是偏向锁标志位

    锁升级机制

    偏向锁

    研究人员发现,大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。 以上是偏向锁的获得和撤销流程,图片来自《并发编程的艺术》,这张是网上找的图,见谅。

    可以看到,线程2CAS操作替换Mark Word,如果不成功,锁才会升级为轻量级锁,如果线程A已经不存在了,那么此时锁状态还是偏向锁。

    轻量级锁

    上图可以看到,轻量级锁的加锁过程如下 首先线程在栈上分配空间,复制Mark Word到栈帧上。然后CAS修改MarkWord,如果成功,将Mark Word上的线程ID指向自己。 解锁过程:会使用CAS操作将Displaced Mark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。

    Java语言实现的锁

    Lock类是在JDK 5 时java/util/currrent包中出现,可以解决synchronized关键字的一些功能限制,例如: synchronized无法中断一个正在等候获得锁的线程,也无法通过投票得到锁。synchronized还要求锁的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行。

    而且对多线程环境中,使用synchronized后,线程要么获得锁,执行相应的代码,要么无法获得锁处于等待状态,对于锁的处理不灵活。而Lock提供了多种基于锁的处理机制。比如:

    void lock(),获取一个锁,如果锁当前被其他线程获得,当前的线程将被休眠。boolean tryLock(),尝试获取一个锁,如果当前锁被其他线程持有,则返回false,不会使当前线程休眠。boolean tryLock(long timeout,TimeUnit unit),如果获取了锁立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁,就返回true,如果等待超时,返回false。void lockInterruptibly(),如果当前线程未被中断,则获取锁,立即返回,将锁的保持计数设置为 1,如果当前线程已获取锁,保持计数+1。如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以 前,该线程将一直处于休眠状态: 锁被其他线程中断、该线程获得锁

    以上内容参考自:https://https://blog.csdn.net/a2225791/article/details/101560871 https://blog.csdn.net/u013851082/article/details/70140223

    LockSupport 类

    LockSupport 类主要作用是挂起和唤醒线程, 该工具类是创建锁和其他同步类的基础。LockSupport 和 CAS 是Java并发包中很多并发工具控制机制的基础,它们底层其实都是依赖Unsafe实现。

    常用方法:

    public static void park(Object blocker); // 暂停当前线程 public static void parkNanos(Object blocker, long nanos); // 暂停当前线程,不过有超时时间的限制 public static void parkUntil(Object blocker, long deadline); // 暂停当前线程,直到某个时间 public static void park(); // 无期限暂停当前线程 public static void parkNanos(long nanos); // 暂停当前线程,不过有超时时间的限制 public static void parkUntil(long deadline); // 暂停当前线程,直到某个时间 public static void unpark(Thread thread); // 恢复指定线程 public static Object getBlocker(Thread t);

    AQS

    AbstractQueueSynchronizer(AQS),抽象同步队列,它是实现同步器的基础组件, 并发包中锁的底层就是使用 AQS 实现的。

    AQS是双向队列,FIFO;竞争资源失败的线程转换成node节点阻塞挂起存放进入 AQS 队列的,Node节点有两种类型,一种是EXCLUSIVE类型(独占)、一种是SHARED(共享)。waitStatus 记录当前线程等待状态(可以为 CANCELLED(线程被取消了)、 SIGNAL (线程需要被唤醒)、 CONDITION (线程在条件队列里面等待〉、 PROPAGATE (释放共享资源时需要通知其他节点〕); prev 记录当前节点的前驱节点, next 记录当前节点的 后继节点。在 AQS 中 维持了 一 个 单 一 的状态信息 state(lock就是基于state实现的),可以通过 getState、 setState、 compareAndSetState 函数修改其值。 对于 ReentrantLock 的实现来说, state可以用来表示 当前线程获取锁的可重入次数 ;对于读写锁 ReentrantReadWriteLock 来说, state 的高 16位表示读状态,也就是获取该读锁的次数,低 16 位表示获取到写锁的线程的可重入次数; 对于 semaphore 来说, state用来表示当前可用信号的个数:对于 CountDownlatch 来说, state 用来表示计数器当前的值。AQS 有个内部类 ConditionObject, 用来结合锁实现线程同步。 ConditionObject 可以 直接访问 AQS对象 内部的变量,比如 state 状态值和 AQS 队列。 ConditionObject 是条件 变量, 每个条件变量对应一个条件队列(单向链表队列),其用来存放调用条件变量的 await 方法后被阻塞的线程,如类图所示, 这个条件队列的头、尾元素分别为自rstWaiter 和last Waiter。

    类结构图,AQS介绍来自:https://blog.csdn.net/qq_35599414/article/details/105489021

    AQS类底层通过CAS实现,具体程序逻辑结合Lock接口深入。

    Lock接口

    Lock接口共声明5个方法,除了上面所说的4个,还有unlock方法。

    他的实现类有6个,其中5个实现类是内部类

    ReentrantLock

    ReentrantLock是一个可重入锁的互斥锁,当锁没有被占有时,调用lock()方法的线程将成功获取锁。可以使用isHeldByCurrentThread()和 getHoldCount()方法来判断当前线程是否拥有该锁。

    ReentrantLock既可以是公平锁又可以是非公平锁,没记错的话JUC定义了两个抽象类,根据fair参数确定锁对象是否是公平的。

    源码分析

    public class ReentrantLock implements Lock, java.io.Serializable { private static final long serialVersionUID = 7373984872572414699L; /** Synchronizer providing all implementation mechanics */ private final Sync sync; /** * Base of synchronization control for this lock. Subclassed * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ //内部类,实现AQS接口 abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = -5179523762034025860L; /** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock(); /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ 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; } 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); } setState(c); return free; } protected final boolean isHeldExclusively() { // While we must in general read state before owner, // we don't need to do so to check if current thread is owner return getExclusiveOwnerThread() == Thread.currentThread(); } final ConditionObject newCondition() { return new ConditionObject(); } // Methods relayed from outer class final Thread getOwner() { return getState() == 0 ? null : getExclusiveOwnerThread(); } final int getHoldCount() { return isHeldExclusively() ? getState() : 0; } final boolean isLocked() { return getState() != 0; } /** * Reconstitutes the instance from a stream (that is, deserializes it). */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); setState(0); // reset to unlocked state } } /** * Sync object for non-fair locks */ //内部类,非公平锁 static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; /** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } } /** * Sync object for fair locks */ static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); } 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; } } //默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //带参构造函数 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); //调用aquire方法 public void lock() { sync.lock(); } public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); } public boolean tryLock() { return sync.nonfairTryAcquire(1); } public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); } public void unlock() { sync.release(1); } public Condition newCondition() { return sync.newCondition(); } public int getHoldCount() { return sync.getHoldCount(); } public boolean isHeldByCurrentThread() { return sync.isHeldExclusively(); } public boolean isLocked() { return sync.isLocked(); } public final boolean isFair() { return sync instanceof FairSync; } protected Thread getOwner() { return sync.getOwner(); } public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } public final boolean hasQueuedThread(Thread thread) { return sync.isQueued(thread); } public final int getQueueLength() { return sync.getQueueLength(); } protected Collection<Thread> getQueuedThreads() { return sync.getQueuedThreads(); } public boolean hasWaiters(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition); } public int getWaitQueueLength(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition); } protected Collection<Thread> getWaitingThreads(Condition condition) { if (condition == null) throw new NullPointerException(); if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject)) throw new IllegalArgumentException("not owner"); return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition); } /** * Returns a string identifying this lock, as well as its lock state. * The state, in brackets, includes either the String {@code "Unlocked"} * or the String {@code "Locked by"} followed by the * {@linkplain Thread#getName name} of the owning thread. * * @return a string identifying this lock, as well as its lock state */ public String toString() { Thread o = sync.getOwner(); return super.toString() + ((o == null) ? "[Unlocked]" : "[Locked by thread " + o.getName() + "]"); } }

    lock.lock()方法相当于进入synchronized同步代码块,用于获取独占锁(应该也可以说是排它锁); 公平锁

    final void lock() { acquire(1); } //AQS的acquie方法 public final void acquire(int arg) { if (!tryAcquire(arg) && //如果获取锁失败,当前线程加入到CLH队列队尾,如果当前线程是老二,再次尝试获取锁 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //如果没拿到锁并且需要interrupt selfInterrupt(); } final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //线程中断标志位 boolean interrupted = false; for (;;) { //上一个节点,因为node相当于当前线程,所以上一个节点表示“上一个等待锁的线程” final Node p = node.predecessor(); /* * 如果当前线程是head的直接后继则尝试获取锁 * 这里不会和等待队列中其它线程发生竞争,但会和尝试获取锁且尚未进入等待队列的线程发生竞争。这是非公平锁和公平锁的一个重要区别。 */ if (p == head && tryAcquire(arg)) { setHead(node); //将当前节点设置设置为头结点 p.next = null; failed = false; return interrupted; } /* 如果不是老二或获取锁失败,则检查是否要阻塞当前线程,是则阻塞当前线程 * shouldParkAfterFailedAcquire:判断“当前线程”是否需要阻塞 * parkAndCheckInterrupt:阻塞当前线程 */ if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } //公平锁的tryAcquire方法 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //锁未被任何线程持有 if (c == 0) { if (!hasQueuedPredecessors() && //只有一个线程能成功 compareAndSetState(0, acquires)) { //设置锁的Owner 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; } //hasQueuedPredecessors方法 看当前队列是否是等待队列中最前面的线程,如果是的就持有锁,不是的话,获取锁失败。 public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. 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()); }

    可以看出,公平锁的hasQueuedPredecessors保证了排队机制。

    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; }

    非公平锁则直接进行CAS操作,破坏排队机制,所以说是非公平的。

    final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }

    另外,非公平锁在acquire(1)之前就尝试CAS获取锁,这里也可能打破排队机制

    await()方法相当于Object.wait()方法,用于阻塞挂起当前线程,当其他线程调用了signal方法(相当于Object.notify()方法)时,被阻塞的线程才会从await处返回。

    lock.newCondition()作用是new一个在AQS内部类ConditionObject对象。每个条件变量内部都维护了一个条件队列,用来存放调用该条件变量的await方法时被阻塞的线程。

    Demo1

    窗口卖票:

    import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class JUCLockUsage implements Runnable{ private static int tickets = 500; private Lock fairLock = new ReentrantLock(true); @Override public void run() { try { fairLock.lock(); while (true) { if (tickets > 0){ System.out.println(Thread.currentThread().getName() + "正在出售第" + (500 - tickets-- + 1) + "张票"); }else { break; } } }finally { fairLock.unlock(); } } public static void main(String[] args) { Thread t1 = new Thread(new JUCLockUsage(), "窗口1"); Thread t2 = new Thread(new JUCLockUsage(), "窗口2"); t1.start(); t2.start(); } } 窗口1正在出售第481张票 窗口2正在出售第482张票 窗口1正在出售第483张票 窗口2正在出售第484张票 窗口1正在出售第485张票 窗口2正在出售第486张票 窗口1正在出售第487张票 窗口2正在出售第488张票 窗口1正在出售第489张票 窗口2正在出售第490张票 窗口1正在出售第491张票 窗口2正在出售第492张票 窗口1正在出售第493张票 窗口2正在出售第494张票 窗口1正在出售第495张票 窗口2正在出售第496张票 窗口1正在出售第497张票 窗口2正在出售第498张票 窗口1正在出售第499张票 窗口2正在出售第500张票

    Demo2

    公平锁

    package atomicClassUsage; import java.util.concurrent.locks.ReentrantLock; public class ReentrentLockUsage implements Runnable{ private static ReentrantLock lock = new ReentrantLock(true); @Override public void run() { while (true){ lock.lock(); try{ System.out.println(Thread.currentThread().getName() + " get lock"); Thread.sleep(1000); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } } public static void main(String[] args) { ReentrentLockUsage fairLock = new ReentrentLockUsage(); Thread thread1 = new Thread(fairLock); Thread thread2 = new Thread(fairLock); thread1.start(); thread2.start(); } } Thread-0 get lock Thread-1 get lock Thread-0 get lock Thread-1 get lock Thread-0 get lock Thread-1 get lock Thread-0 get lock Thread-1 get lock

    Demo3

    非公平锁

    package atomicClassUsage; import java.util.concurrent.locks.ReentrantLock; public class ReentrentLockUsage implements Runnable{ private static ReentrantLock lock = new ReentrantLock(false); @Override public void run() { while (true){ lock.lock(); try{ System.out.println(Thread.currentThread().getName() + " get lock"); Thread.sleep(1000); }catch (Exception e){ e.printStackTrace(); }finally { lock.unlock(); } } } public static void main(String[] args) { ReentrentLockUsage fairLock = new ReentrentLockUsage(); Thread thread1 = new Thread(fairLock); Thread thread2 = new Thread(fairLock); thread1.start(); thread2.start(); } } Thread-0 get lock Thread-1 get lock Thread-0 get lock Thread-0 get lock Thread-0 get lock Thread-0 get lock
    Processed: 0.016, SQL: 8