1 显式锁实现
synchronized关键字存在的缺陷,其中,当某个线程在争抢对象监视器(object monitor)的时候将会进入阻塞状态,并且是无法被中断的,也就是说synchronized关键字并未提供一种获取monitor锁失败的通知机制,执行线程只能等待其他线程释放该monitor的锁进而得到一次机会,本节将借助于AtomicBoolean实现一个可立即返回并且退出阻塞的显式锁Lock
关于synchronized关键字存在的缺陷可参考:自定义显式锁BooleanLock
public class TryLock {
/**
* 定义一个AtomicBoolean并且使其初值为false
* 表示当前的锁是否被任何线程获得,true表示被线程获得了
*/
private final AtomicBoolean ab = new AtomicBoolean(false);
/**
* 线程保险箱,用于存放与线程上下文关联的数据副本
*/
private final ThreadLocal<Boolean> threadLocal = ThreadLocal.withInitial(() -> false);
/**
* 可理解返回的lock方法
*/
public boolean tryLock() {
// 借助于AtomicBoolean的CAS操作对布尔值进行修改
boolean result = ab.compareAndSet(false, true);
if (result) {
// 修改成功的时候,同步更新threadLocal的数据副本值
threadLocal.set(true);
}
return result;
}
/**
* 锁的释放
*
* @return
*/
public boolean release() {
// 判断调用release方法的线程是否成功获得了该锁
if (threadLocal.get()) {
// 获取到锁了
// 标记锁被释放,并且原子性地修改布尔值为false
threadLocal.set(false);
return ab.compareAndSet(true, false);
}else {
// 直接返回
return false;
}
}
}
-
定义了一个AtomicBoolean类型的属性ab,其初始值为false,表明当前的锁未被任何线程获得,也就是说某线程可以成功获得对该锁的持有。
-
我们定义了一个
ThreadLocal<Boolean>
,并且重写其初始化方法返回false,该ThreadLocal的使用在TryLock中非常关键,我们都知道显式锁为了确保锁能够被正确地释放,一般会借助于try…finally语句块以确保release方法能够被执行,因此为了防止某些未能成功获取锁的线程在执行release方法的时候改变ab的值,我们需要借助于ThreadLocal<Boolean>
中的数据副本进行标记和判断。 -
我们使用AtomicBoolean的compareAndSet方法对ab当前的布尔值进行CAS操作,当预期值与ab当前值一致时操作才能成功,否则操作将直接失败,因此执行该方法的线程不会进入阻塞,这一点很关键
-
如果某线程成功执行了对ab当前布尔值的修改,那么我们需要将其在
ThreadLocal<Boolean>
关联的数据副本标记为true,以标明当前线程成功获取了对TryLock的持有。 -
release方法需要秉承一个原则,那就是只有成功获得该锁的线程才有资格对其进行释放,反映到我们的代码中就是执行对ab当前值布尔值的更新动作
-
确认当前有资格进行锁的释放以后,就可以对ab当前布尔值进行更新操作了,并且标记当前线程已将锁释放
2 测试使用
package study.wyy.juc.atom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;
/**
* @author wyaoyao
* @date 2021/4/19 15:31
* 在下面的代码中,我们启动的10个线程在一个while死循环中不断地进行锁的获得以及释放过程,
* 运行上面的代码不难看出,在同一时刻只会有一个线程能够成功获得对该锁的持有。
*/
public class TryLockTest {
private final static Object VAL_OBJ = new Object();
public static void main(String[] args) {
// 定义TryLock锁
final TryLock lock = new TryLock();
final List<Object> validation = new ArrayList<>();
// 启动10个线程,并且不断地进行锁的获取和释放动作
for (int i = 0; i < 10; i++){
new Thread(() ->{
while (true){
try {
// 尝试获取该锁,该方法并不会导致当前线程进入阻塞
if(lock.tryLock()){
System.out.println(currentThread() + ": get the lock.");
// 进行校验,以确保validation中只存在一个元素
if (validation.size() > 1) {
throw new IllegalStateException("validation failed.");
}
validation.add(VAL_OBJ);
TimeUnit.MILLISECONDS.sleep(current().nextInt(10));
}else {
// 未获得锁,简单做个休眠,以防止出现CPU过高电脑死机的情况发生
TimeUnit.MILLISECONDS.sleep(current().nextInt(10));
}
}catch (Exception e){
e.printStackTrace();
}finally {
// 在finally语句块中进行锁的释放操作
if (lock.release()) {
System.out.println(currentThread() + ": release the lock.");
validation.remove(VAL_OBJ);
}
}
}
}).start();
}
}
}