如何理解Lock

article/2025/9/20 13:12:30

显示锁

JDK层面提供了Lock锁都是通过Java提供的接口来手动解锁和释放锁的,所以在某种程度上,JDK中提供的Lock锁也叫显示锁、JDK提供的显示锁位于java.util.concurrent.locks包下,Lock接口的源码如下:

public interface Lock {void lock();void lockInterruptibly() throws InterruptedException;boolean tryLock();boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void unlock();Condition newCondition();
}
  • lock()方法:阻塞模式抢占锁的方法。如果当前前程抢占锁成功,则继续向下执行程序的业务逻辑,否则,当前线程会阻塞。直到其他抢占到锁的线程释放锁后再继续抢占锁。
  • lockInterruptibly()方法:可中断模式抢占锁的方法,当前线程在调用lockInterruptibly()方法抢占锁的过程中,能够相应中断信号,从而能够终端当前线程。
  • tryLock()方法:非阻塞模式下尝试抢占锁的方法。当前线程调用tryLock()方法时,现成不会阻塞,而会立即返回抢占锁的结果。抢占锁成功会返回true,抢占锁失败则会返回false。此方法会抛出InterruptedException异常。
  • tryLock(long time,TimeUnit unit)方法:在指定的时间内抢占锁的方法。当前线程如果在指定的时间内抢占锁成功,则返回true。如果在指定的时间内抢占锁失败,或者超出指定时间为抢占到锁,则返回false。当前线程在抢占锁的过程中可以响应中断信号。此方法会抛出InterruptedException异常。
  • unLock()方法:释放锁的方法,当前线程加锁成功后,在执行完程序的业务逻辑后,调用此方法来释放锁。
  • newCondition()方法: 此方法用于创建与当前绑定的Condition对象,主要用于线程间以“等待-通知”的方式进行通信。

公平锁和非公平锁

公平锁原理

公平锁的核心就是对争抢锁的所有线程都是公平的,在多线程并发环境下,每个线程在抢占锁的过程中,都会首先检查锁维护的等待队列。如果等待队列为空,当着当前线程是等待队列中的第一个线程,则当前线程会获取到锁,否则,当前线程会加入等待队列的尾部,然后队列中的线程会按照先进先出的规则顺序获取锁资源。
线程抢占公平锁的流程如图所示:
在这里插入图片描述
从上图可以看出,公平锁的等待队列中存在线程1,2,3三个线程,并且线程1存在等待队列的头部,线程3存放在等待队列的尾部。
此时,有线程4尝试直接获取公平锁,但线程4在抢占公平锁时,会首先判断锁对应的等待队列是否存在元素。很显然,此时等待队列中存在3个线程。因此,线程4会进入等待队列的尾部,排在线程3的后面。等待队列的线程会按照先进先出的顺序依次出队,获取公平锁。也就是说,线程4会在线程3后面,在等待队列中最后一个出队获取公平锁。

ReentrantLock中的公平锁

ReentrantLock实现了公平锁机制,在ReentrantLock类中,提供了一个带有boolean类型参数的构造方法,源码如下:

    /*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

在这个构造方中,如果传入的参数为true,就会创建一个FairSync对象赋值给ReentrantLock类的成员变量sync,此时线程获取的锁就是公平锁。fairSync时ReentrantLock类中提供的一个表示公平锁的静态内部类,源码如下:

    /*** Sync object for fair locks*/static final class FairSync extends Sync {private static final long serialVersionUID = -3000897897090466540L;final void lock() {acquire(1);}/*** Fair version of tryAcquire.  Don't grant access unless* recursive call or no waiters or is first.*/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;}}

可以看出,FairSync类的核心就是调用AQS的模版方法进行线程的入队和出队操作,FairSync类的方法lock()方法会调用AQS的acquire()方法,AQS的acquire()方法会调用tryAcquire()方法,而AQS中的tryAcquire()方法实际上是基于子类实现的,因此,此时调用的还是FairSync类的方法。原因是FairSync类继承了Sync类,而Sync类直接继承了AQS。Sync类是ReentrantLock类中的一个静态抽象内部类,源码如下:

    /*** 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.*/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) // overflowthrow 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 ownerreturn getExclusiveOwnerThread() == Thread.currentThread();}final ConditionObject newCondition() {return new ConditionObject();}// Methods relayed from outer classfinal 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}}

回到FairSync类中,FairSync类的tryAcquire()方法先通过hasQueuedPredecessors()方法判断队列中是否存在后继结点,如果队列中存在后继结点,并且当前线程未占用锁资源,则tryAcquire()方法会返回false,当前线程会进入等待队列的尾部排队。
ReentrantLock中公平锁的加锁流程中方法调用的逻辑如下图所示:
在这里插入图片描述
由上图可以看出,使用ReentrantLock的公平锁,当某个线程调用ReentrantLock的Lock()方法加锁时,会经历如下的加锁流程。

  1. 在某个线程调用ReentrantLock的lock()方法时,ReentrantLock的lock()方法会先调用FairSync的lock()方法。
  2. FairSync的lock()方法调用AQS的acquire()方法获取资源。
  3. AQS的acquire()方法会先调用FairSync的tryAcquire()方法尝试获取资源。
  4. 在AQS中的acquire()方法嗲用acquireWaiter()方法,将当前线程封装成Node节点加入队列的尾部。
  5. 在AQS中的acquire()方法中调用acquireQueued()方法使线程在等待队列中排队。

非公平锁原理

非公平锁的核心就是对抢占锁的所有线程都是不公平的,在多线程并发环境下,每个线程在抢占锁的过程中都会先直接尝试抢占锁,如果抢占锁成功,就继续执行程序的业务逻辑,如果抢占锁失败,就会进入等待队列中排队。
公平锁与非公平锁的核心区别在于对排队的处理上,非公平锁在队列的队首位置可以进行一次插队操作,插队成功就可以获取锁,插队失败就会像公平锁一样进入等待队列排队。在非公平锁环境下,可能会出现某个线程在队列中等待时间过长而一直无法获取锁的现象,这种现象叫做饥饿效应。
虽然非公平锁会产生饥饿效应,但是非公平锁比公平锁性能更优。
线程抢占非公平锁的流程如图:
在这里插入图片描述

由上图可以看出,非公平锁对应的等待队列中存在线程1,2,3三个线程。其中线程1在队列的头部,说明线程1已经获取到锁。线程3在队列的尾部。此时线程4尝试获取非公平锁,也就是尝试插入队列的头部。
当线程4插入队列的头部成功时,线程1已经执行完业务逻辑并释放锁,线程4获取到锁,线程3位于等到队列的尾部。
当线程4插入队列的头部失败时,线程2位于队列的头部,线程2会获取到锁。线程4会插入队列的尾部。

ReentrantLock中的非公平锁

ReentrantLock中默认实现的就是非公平锁,例如,调用ReentrantLock的无参构造函数创建的锁对象就是非公平锁,ReentrantLock的无参构造函数源码如下:

    /*** Creates an instance of {@code ReentrantLock}.* This is equivalent to using {@code ReentrantLock(false)}.*/public ReentrantLock() {sync = new NonfairSync();}

也可以通过ReentrantLock的有参构造函数,传入false来创建非公平锁,源码如下:

    /*** Creates an instance of {@code ReentrantLock} with the* given fairness policy.** @param fair {@code true} if this lock should use a fair ordering policy*/public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}

无论是调用ReentrantLock类的无参构造函数,还是调用ReentrantLock的有参构造函数并传入false,都会创建一个NonfairSync类的对象并赋值给ReentrantLock类的成员变量sync,此时创建的就是非公平锁。
NonfairSync类是ReentrantLock类中的一个静态内部类,源码如下:

    /*** 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());elseacquire(1);}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}

由上述源码可以看出,在非公平锁的加锁逻辑中,并没有直接将线程放入等待队列的尾部,而是先尝试将当前线程插入等待队列的头部,也就是先尝试获取锁资源。如果获取锁资源成功,则继续执行程序的业务逻辑。如果尝试获取锁资源失败,则调用AQS的acquire()方法获取资源。
同样,NonfairSync类继承了Sync类,而Sync继承了AQS,所以,NonfairSync类在加锁流程的本质上,还是调用了AQS类的模版代码实现入队和出队操作。
AQS的acquire()方法会回调NonfairSync类中的tryAcquire()方法,而在NonfairSync类的tryAcquire()方法中,又会调用Sync类中的nonfairTryAcquire()方法尝试获取锁,nonfairTryAcquire()方法的源码如下:

        /*** 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) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}

可以看出,在nonfairTryAcquire()方法中,并没有将线程加锁加入等待队列中,只是对锁的状态进行了判断,根据不同的状态进行相应的操作。当锁的状态标识为0时,就直接尝试获取锁,然后执行setExclusiveOwnerThread()方法,不会处理等待队列中的排队节点的逻辑。
ReentrantLock中非公平锁的加锁流程中方法调用的逻辑如下图所示:
在这里插入图片描述
由上图可以看出,使用ReentrantLock的非公平锁,当某个线程调用ReentrantLock的lock()方法加锁时,会经历如下的加锁流程。

  1. 在某个线程调用ReentrantLock的lock()方法加锁时,ReentrantLock的lock()方法会先调用NonfairSync()类中的lock()方法
  2. NonfairSync类的lock()方法会调用AQS的acquire()方法获取资源
  3. 在AQS的acquire()方法中会回调NonfairSync类中的tryAcquire()方法尝试获取资源
  4. 在NonfairSync类中的tryAcquire()方法中调用Sync类的nonfairTryAcquire()方法尝试获取资源
  5. AQS中的acquire()方法调用addWaiter()方法将当前线程封装成Node节点放入等待队列
  6. 在AQS中的acquire()房中调用acquireQueued()方法使线程在等待队列中排队。

悲观锁与乐观锁

悲关锁原理

悲观锁是一种设计思想,顾名思义,它的核心思想就是对于事物保持有悲观的态度,每次都会按照最坏的情况下执行。也就是说,在线程获取数据的时候,总是认为其他的线程会修改数据。所以在线程每次获取数据时都会加锁,在此期间其他线程要想获取相同的数据,则会阻塞,直到获取锁的线程释放锁,当前线程加锁成功后,才能获取到相同的数据。
java提供的synchronized内置锁就是一种悲观锁的实现,而ReentrantLock在一定程度上也是悲观锁的实现,悲观锁存在如下问题。

  1. 在多线程并发环境下,悲观锁的加锁和释放锁操作会产生大量的CPU线程切换,耗费CPU资源,导致CPU性能低下。
  2. 当某个线程抢占到锁后,会导致其他所有未抢占到当前锁的线程阻塞挂起,影响线程的执行性能。
  3. 假设存在线程A和线程B两个线程,线程B的优先级比线程A的优先级高。但是在业务执行的过程中,线程A抢占到锁之后,线程B才创建执行业务逻辑。因此,当线程B抢占与线程A相同的锁时会被阻塞,从而出现优先级高的线程等待优先级低的线程释放锁的现象,导致优先级高的线程无法快速完成任务。

悲观锁实战源代码

package com.lifly;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;/*** @author lifly* @description* @date 2023-03-30 22:02* @versoin 1.0.0**/
public class PessimismLockTest {private Lock lock = new ReentrantLock();public void lockAndUnlock(){try {lock.lock();System.out.println(Thread.currentThread().getName()+"抢占锁成功!");}finally {lock.unlock();}}public static void main(String[] args) {PessimismLockTest pessimismLockTest = new PessimismLockTest();IntStream.range(0,5).forEach((i) ->{new Thread(()->{System.out.println(Thread.currentThread().getName()+"开启抢占锁");pessimismLockTest.lockAndUnlock();}).start();});}
}

在这里插入图片描述

乐观锁原理

与悲观锁一样,乐观锁也是一种设计思想,其核心思想就是乐观。线程在每次获取数据时,都会认为其他线程不会修改数据,所以不会加锁,但是当前线程在更新数据时会判断当前数据在此期间有没有被其他线程修改过。乐观锁可以使用版本号机制实现,也可以使用CAS机制实现。
乐观锁更适合用于读多写少的场景,可以提高系统的性能。在java中java.util.concurrent.atomic包下原子类,就是基于CAS乐观锁实现的。

乐观锁实战源代码

package com.lifly;import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;/*** @author lifly* @description* @date 2023-03-30 22:08* @versoin 1.0.0**/
public class OptimisticLockTest {private AtomicInteger atomicInteger = new AtomicInteger();public void incrementCount(){atomicInteger.incrementAndGet();}public int getCount(){return atomicInteger.get();}public static void main(String[] args) throws InterruptedException {OptimisticLockTest optimisticLockTest = new OptimisticLockTest();IntStream.range(0,10).forEach((i)->{new Thread(()->{optimisticLockTest.incrementCount();}).start();});Thread.sleep(500);int count = optimisticLockTest.getCount();System.out.println("最终的结果为:"+count);}
}

在这里插入图片描述

中断锁与不可中断锁

可中断锁指多个线程抢占的过程中可以被中断的锁,不可中断锁指在多个线程抢占的过程中不可以被中断的锁。

可中断锁原理

Java的JUC包提供的显示锁,如ReentrantLock,就是可中断锁,支持在抢占的过程中中断锁。
在java提供的Lock接口中,有两个方法抛出了InterruptedException异常。如下:

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;void lockInterruptibly() throws InterruptedException;

这两个方法在加锁的过程中可以中断锁。具体的逻辑如下:

  1. lockInterruptibly()方法的中断逻辑:在抢占锁的过程中会处理由Thread类中的interrupt()方法发出的中断信号,如果当前线程在抢占的过程中被中断,就会抛出InterruptedException异常并终止抢占锁的过程。
  2. tryLock(long time,TimeUnit unit)方法的中断逻辑:尝试在指定的时间内阻塞式的抢占锁,在抢占锁的过程中会处理由Thread类中的interrupt()方法发出的中断信号,如果当前线程在抢占锁的过程中被中断,就会抛出InterruptedException异常并终止抢占锁的过程。
    可以看出,无论是lockInterruptibly()方法,还是tryLock(long time,TimeUnit unit)方法在抢占锁的过程中,都是通过处理由Thread类中的interrupt()发出的中断信号来处理中断事件的。

可中断锁实战源代码

{private Lock lock = new ReentrantLock();public void lockAndUnlock(){try {lock.lockInterruptibly();System.out.println(Thread.currentThread().getName()+"抢占锁成功");if (Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName()+"被中断");}Thread.sleep(1000);}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"抢占锁被中断");}finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {InterruptiblyLockTest interruptiblyLockTest = new InterruptiblyLockTest();Thread threadA = new Thread(()->{interruptiblyLockTest.lockAndUnlock();},"ThreadA");Thread threadB = new Thread(()->{interruptiblyLockTest.lockAndUnlock();},"ThreadB");threadA.start();threadB.start();Thread.sleep(100);threadA.interrupt();threadB.interrupt();Thread.sleep(2000);}}

在这里插入图片描述
从结果中可以看出,ThreadA抢占锁成功后在休眠的过程中被中断,ThreadB线程在等待加锁时也会被中断,也就是在抢占锁的过程中被中断,线程被中断后,会捕获到InterruptedException异常。说明ReentrantLock中的lockInterruptibly()方法获取的是一种可中断锁。

不可中断锁

不可中断锁指线程在抢占锁的过程中不能被中断。也就是说,线程在抢占不可中断锁时,如果抢占成功,则继续执行业务逻辑;如果抢占失败,则阻塞挂起。线程在阻塞挂起的过程中不能被中断。java中的synchronized锁就是不可中断锁。

不可中断锁源代码

package com.lifly;/*** @author lifly* @description* @date 2023-03-30 22:24* @versoin 1.0.0**/
public class NonInterruptiblyLockTest {public synchronized void lock(){try {System.out.println(Thread.currentThread().getName()+"抢占锁成功");if (Thread.currentThread().isInterrupted()){System.out.println(Thread.currentThread().getName()+"被中断");}Thread.sleep(1000);}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"抢占锁被中断");}}public static void main(String[] args) throws InterruptedException {NonInterruptiblyLockTest nonInterruptiblyLockTest = new NonInterruptiblyLockTest();Thread threadA = new Thread(()->{nonInterruptiblyLockTest.lock();},"ThreadA");Thread threadB = new Thread(()->{nonInterruptiblyLockTest.lock();},"ThreadB");threadA.start();threadB.start();Thread.sleep(100);threadA.interrupt();threadB.interrupt();Thread.sleep(2000);}
}

在这里插入图片描述
从输出结果中可以看出,无论是threadA还是threadB线程,都是在抢占锁成功后被中断的,在抢占锁的过程中不会被中断,说明synchronized是一种不可中断锁。

排它锁与共享锁

排它锁原理

排它锁也叫独占锁或互斥锁,排它锁在同一时刻只能被一个线程获取到。某个线程获取到排它锁后,其他线程想再获取同一个资源,只能阻塞等待,直到获取锁的线程释放锁。
java中提供的synchronized锁和ReentrantLock锁都是排它锁的实现。另外,ReadWriteLock中的写锁也是排它锁。

共享锁原理

共享锁在同一时刻能够被多个线程获取到。需要注意的是,多个线程同时获取到共享锁后,只能读取临界区的数据,不能修改临界区的数据。也就是说,共享锁时针对读操作的锁。
在java中ReadWriteLock中的读锁,Semaphore类和CountDownLatch类都实现了在同一时刻允许多个线程获取到锁,是共享锁的实现。

可重入锁

可重入锁原理

可重入锁表示一个线程能够对相同的资源重复加锁。也就是说,同一个线程能够多次进入使用同一个锁修饰的方法或代码块。需要注意的是,在线程释放锁时,释放锁的次数需要与加锁的次数相同,才能保证线程真正释放勒索。例如线程A加锁时执行了3次加锁操作,释放锁就必须执行3次释放锁操作。
在ReentrantLock的内部类Sync的nonfairTryAcquire()方法中,如下代码是ReentrantLock实现可重入锁的关键代码

 else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}

在上述代码中,当前线程已经抢占有锁时,会判断当前线程是否是已经获取锁的线程,如果当前线程是已经获取过锁的线程,则增加内部的状态计数,以此实现锁的可重入性。
当ReentrantLock对象解锁时,会先调用AQS的release()方法,而AQS的release()方法又会调用ReentrantLock的内部类Sync的tryRelease()方法,ReentrantLock的内部类Sync的tryRelease()方法的源码如下

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

在ReentrantLock的内部类Sync的tryRelease()方法中,首先对状态计数减去传入的值,判断当前线程是否已经获取到锁的线程,如果当前线程时已经获取到锁的线程,直接抛出IllegalMonitorStateException异常。然后定义一个是否成功释放锁的变量free,默认值为false,接下来判断state状态计数的值是否减为0.如果state状态计数的值已经减为0,则说明当前线程已经完全释放锁,此时的锁处于空闲状态,将是否成功释放锁的变量free设置为0,并将当前拥有锁的线程置为null。最后,设置锁的状态标识,返回free,结果会返回true。
如果state状态计数的值没有减为0,则说明当前线程并没有完全释放锁,此时的free变量为false,返回free,结果会返回false.
所以,在ReentrantLock中,可重入锁的加锁操作会累加状态计数,解锁操作会累减状态计数。
java中的synchronized锁和ReentrantLock锁都实现了可重入性。

可重入锁实战源代码

package com.lifly;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;/*** @author lifly* @description* @date 2023-04-05 19:50* @versoin 1.0.0**/
public class ReentrantLockTest {private Lock lock = new ReentrantLock();/*** 加锁并释放锁*/public void lockAndUnlock(){try {lock.lock();System.out.println(Thread.currentThread().getName()+"第一次抢占锁成功");lock.lock();System.out.println(Thread.currentThread().getName()+"第二次抢占锁成功");}finally {lock.unlock();System.out.println(Thread.currentThread().getName()+"第一次释放锁成功");lock.unlock();System.out.println(Thread.currentThread().getName()+"第二次释放锁成功");}}public static void main(String[] args) {ReentrantLockTest reentrantLockTest = new ReentrantLockTest();IntStream.range(0,2).forEach((i)->{new Thread(()->{System.out.println(Thread.currentThread().getName()+"开启抢占锁");reentrantLockTest.lockAndUnlock();}).start();});}
}

在这里插入图片描述
上述案例的输出结果说明了线程在获取可重入锁,释放锁成功的次数与加锁成功的次数相同时,才能完全释放锁,其他线程才能获取到相同的锁。
synchronized锁实现可重入锁源代码:

package com.lifly;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.IntStream;/*** @author lifly* @description* @date 2023-04-05 20:04* @versoin 1.0.0**/
public class SynchronizedLockTest {/*** 加锁并释放锁*/public void lockAndUnlock(){synchronized (this){System.out.println(Thread.currentThread().getName()+"第一次抢占锁成功");synchronized (this){System.out.println(Thread.currentThread().getName()+"第二次抢占锁成功");}System.out.println(Thread.currentThread().getName()+"第一次释放锁成功");}System.out.println(Thread.currentThread().getName()+"第二次释放锁成功");}public static void main(String[] args) {ReentrantLockTest reentrantLockTest = new ReentrantLockTest();IntStream.range(0,2).forEach((i)->{new Thread(()->{System.out.println(Thread.currentThread().getName()+"开启抢占锁");reentrantLockTest.lockAndUnlock();}).start();});}
}

在这里插入图片描述


http://chatgpt.dhexx.cn/article/PjcrPv5L.shtml

相关文章

锁(Locks)

锁(Locks) 1 ReentrantLock 应用demo 可重入锁&#xff0c;是一种使用递归无堵塞的同步机制 比 synchronized 更强大、更灵活的锁机制&#xff0c;可以减少死锁发生的概率 默认为非公平锁&#xff0c;可以自定义为公平锁 底层采用 AQS 实现&#xff0c;通过内部 Sync 集成…

lock锁

目录 1. lock 基本用法 2. lock公平锁与非公平锁 3. lock注意事项 4. synchronized 与 lock区别 1. lock 基本用法 lock.lock(); try {} finally {lock.unlock() }或者try {lock.lock(); } finally {lock.unlock() }public class ThreadLock1 {public static void main(S…

LOCKED勒索病毒解密 数据恢复

什么是LOCKED勒索病毒 LOCKED勒索病毒是由Michael Gillespie发现的。该恶意程序旨在通过加密来阻止对存储在计算机上的文件的访问。为了解密他们的文件&#xff0c;鼓励受害者购买解密工具。与大多数此类程序一样&#xff0c;[LOCKED] 重命名所有加密文件&#xff0c;在本例中…

服务器数据中了locked勒索病毒,有关locked勒索病毒的介绍与预防建议

随着网络的普及和科技技术的发展&#xff0c;网络安全问题日益突出。而其中&#xff0c;勒索病毒就是一种常见的网络安全威胁。一旦企业的服务器数据库被勒索病毒攻击&#xff0c;会导致企业内部的重要数据被加密&#xff0c;给工作和生产生活带了极大的困扰。下面就为大家介绍…

locked 勒索软件

1.Locked介绍 Locked病毒属于Void Crypt 勒索软件家族。该勒索软件会加密 PC 上的所有用户数据&#xff08;照片、文档、Excel 表格、音乐、视频等&#xff09;&#xff0c;将其特定扩展名添加到每个文件&#xff0c;并在每个包含加密文件的文件夹中创建文件。 2.我是如何在我…

MEID

MEID简介 Mobile Equipment IDentifier&#xff08;MEID&#xff09;是全球唯一的56bit移动终端标识号。标识号会被烧入终端里&#xff0c;并且不能被修改。可用来对移动式设备进行身份识别和跟踪。由于ESN号段是有限的资源&#xff0c;基本上耗尽&#xff0c;可能还有少量回收…

科谱|MEID表格如何填写,99开头,MEID怎么申请,MEID申请表填写

作为通信产品最常见的三大主流号码之一MEID最近这些年的存在感确实偏低了点&#xff0c;尽管如此还是有大量需要使用MEID号码的产品&#xff0c;及时快速准确的申请到号码还是必不可少的。 文化的差异和语言的不通会造成不大不小的麻烦&#xff0c;今天我们结合最新版的申请表…

关于安卓系统4.0/5.0/6.0获取单卡手机,双卡手机的imei1,imei2,meid(用反射来实现,史上最详细,最全面获取)--binbinyang

有的人问我要代码跟例子&#xff0c;上次在GITHUB上弄了一个&#xff0c;提供地址 给大家 https://github.com/binbinyYang/GetPhoneInfo https://github.com/binbinyYang/GetPhoneInfo -------------------------------------------------------- 最近这3天&#xff0c;一直在…

MEID 的构成

MEID 的构成如下&#xff0c;针对 Hex 格式&#xff1a; 最后一位是 CD&#xff0c;这个 CD 不是 MEID 的组成部分&#xff0c;真正的 MEID 是前 14 位。在手机与基站进行 MEID 检查时&#xff0c;手机提交的 MEID 不能包含 CD 位&#xff0c;否则就会出错。当初设计此 CD 位主…

Mina MEID/GSM Activator 1.0 三网信号激活,支持iOS12.0~14.8.1

Mina团队已经更新工具&#xff0c;现在支持MEID/GSM三网和两网解锁信号&#xff01;支持iOS14.8系统&#xff0c;两网价格和三网价格一样。 Mina MEID/GSM Activator可以激活所有MEID/GSM二网、三网恢复信号&#xff0c;并且支持打电话、短信、4G流量上网&#xff0c;支持iPhon…

高通芯片联机读取修改串码 meid ESN wifi 蓝牙 sn等参数的操作解析{二}

上次我发了几个相关联机读写参数的帖子。很多友友询问有没有其他相关软件来解读参数的教程。今天就来个续集来解析参数读写 关于安卓机型写串码 改串码 端口开启和基带qcn等一些经验 高通联机修改IMEI等参数的相关解析 高通芯片基带相关的软件 QPST QXDM DFS等等&#xff0c…

说说移动设备的各种标识码(DeviceID, IMEI, UUID, UDID, OAID, IDFA, GAID)

转战广告行业&#xff0c;收集整理一波移动设备各种标识码的含义当做基础知识储备 一、名词解释 Device ID&#xff1a;设备ID。IMEI&#xff1a;&#xff08;International Mobile Equipment Identity&#xff09;国际移动设备标识的缩写。是由15位数字组成的“电子串号”&a…

Mina MEID/GSM Activator 1.0 三网信号激活,支持12.5.3~14.7

Mina团队已经更新工具&#xff0c;现在支持MEID/GSM三网和两网解锁信号&#xff01;支持iOS14.7系统&#xff0c;两网价格和三网价格一样。 Mina MEID/GSM Activator可以激活所有MEID/GSM二网、三网恢复信号&#xff0c;并且支持打电话、短信、4G流量上网&#xff0c;支持iPhon…

Mina MEID Activator 2.120210512更新使用说明支持三网(移动、联通、电信)国行版手机解锁打电话4G苹果手机激活锁停用Hello密码锁绕ID屏幕锁密码

Mina MEID Activator 是由Minacriss开发的新工具。可以激活所有MEID三网的信号恢复&#xff0c;并且支持打电话、短信、4G流量上网。支持iPhone5sX直接所有型号&#xff0c;支持iOS12.5.2iOS14.4.2。MEID三网指&#xff1a;不小心忘记ID密码&#xff0c;而且已经刷机并且是激活…

关于IMEI、MEID、IMSI

关于IMEI、MEID、IMSI 简介 IMEI、MEID都是用于标识一台物理设备的ID信息。在Android 8.0以下系统提供的API中&#xff0c;会根据不同条件返回二者之一的信息。 IMEI&#xff1a; 国际移动设备识别码&#xff0c;是区别移动设备的标志&#xff0c;一般用于标识某一台独立的设…

2022最新手机设备标识码(IMEI、MEID、UDID、UUID、ANDROID_ID、GAID、IDFA等)教程

Android篇 1 IMEI和MEID (1) IMEI (International Mobile Equipment Identity) 是国际移动设备身份码的缩写&#xff0c;国际移动装备辨识码&#xff0c;只有Android手机才获取的到&#xff0c;是由15位数字组成的"电子串号"&#xff0c;比如像这样 35988103031435…

安卓唯一标识:IMEI,MEID,MAC地址,Android_id,UUID,OAID

目录 IMEI&#xff1a; MEID&#xff1a; MAC地址&#xff1a; Android_id&#xff1a; UUID&#xff1a; OAID&#xff1a; 安卓设备的唯一标识&#xff0c;获取的目的在于数据统计&#xff0c;广告归因分析等用途。常用作唯一标识的有IMEI&#xff0c;MEID&#xff0c;…

QA:MEID号申请、什么是MEID号、怎么申请MEID号、MEID号获取

本文671个字&#xff0c;正常大约需要阅读两分钟&#xff0c;完整阅读更精彩&#xff0c;欢迎点赞、转发或留言&#xff01; Q&#xff1a;什么是MEID号&#xff1f; A&#xff1a; MEID 移动设备识别码(Mobile Equipment Identifier)是CDMA手机的身份识别码&#xff0c;也是…

什么是MEID号如何申请MEID,A0、A1、A2以及99开头的MEID号的区别?

MEID/ESN号段ESN Manufacturer’s (MFR) Codes (ESN 厂家代码段) 主要适用于CDMA类的产品Mobile Equipment IDentifier&#xff08;MEID&#xff09;是全球唯一的56bit移动终端标识号需要向美国申请。标识号会被输入终端里&#xff0c;无法修改。可用来对移动式设备进行身份识别…

如何深入理解 StatsD 与 Graphite ?

众所周知&#xff0c;StatsD 负责收集并聚合测量值。之后&#xff0c;它会将数据传给 Graphite&#xff0c;后者以时间序列为依据存储数据&#xff0c;并绘制图表。但是&#xff0c;我们不知道&#xff0c;基于 http 访问的图表在展示时&#xff0c;是基于每秒钟的请求数&#…