由上一篇 synchronized 理解可以知道,synchronized 提供了一种排他式的数据同步机制,某个线程在获取 monior lock 的时候可能会被阻塞,而这种阻塞有两个很明显的缺陷:
- 无法控制阻塞时长
- 阻塞不能被中断
所以此篇文章就实现一个具备 synchronized 关键字所有功能的同时又具备可中断和 lock 超时的功能。
话不多说,开始看接口
import java.util.List;
import java.util.concurrent.TimeoutException;
/**
* @author hasaki_w_c
* @version 1.0
* @date 2021/9/26 15:56
*/
public interface Lock {
/**
* 此 lock() 方法永远阻塞,除非获取到了锁,但可以被中断
* @throws InterruptedException 中断时抛此异常
*/
void lock() throws InterruptedException;
/**
* 可中断,并且有超时功能
* @param mills 超时时长
* @throws InterruptedException 中断抛此异常
* @throws TimeoutException 超时抛此异常
*/
void lock(long mills) throws InterruptedException, TimeoutException;
/**
* 释放锁的方法
*/
void unlock();
/**
* 获取当前哪些线程被阻塞
* @return 阻塞线程列表
*/
List<Thread> getBlockedThreads();
}
然后是实现类
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
/**
* @author hasaki_w_c
* @version 1.0
* @date 2021/9/26 16:25
*/
public class BooleanLock implements Lock{
private Thread currentThread;
private boolean locked = false;
private final List<Thread> blockedList = new ArrayList<>();
@Override
public void lock() throws InterruptedException {
synchronized (this) {
//如果当前锁已经被某个线程获得,则该线程加入阻塞队列,并且使当前线程 wait 释放对 this monitor 的所有权
while (locked) {
blockedList.add(Thread.currentThread());
this.wait();
}
//如果当前锁没有被其他线程获得,则该线程将尝试从阻塞队列中删除自己
blockedList.remove(Thread.currentThread());
//locked 开关被指定为 true
this.locked = true;
//记录获取锁的线程
this.currentThread = Thread.currentThread();
}
}
@Override
public void lock(long mills) throws InterruptedException, TimeoutException {
synchronized (this) {
//判断是否合法,不合法直接调用 lock() 方法
if (mills <= 0) {
this.lock();
}else {
long remainMills = mills;
long endMills = System.currentTimeMillis() + remainMills;
while (locked) {
//如果 remainMills 小于等于 0,则意味着当前线程被其他线程唤醒或者在指定的 wait 时间到了之后还没有获得锁,
// 这种情况下会抛出超时的异常
if (remainMills <= 0) {
throw new TimeoutException("can't get the lock during " + mills + " ms.");
}
if (!blockedList.contains(Thread.currentThread())) {
blockedList.add(Thread.currentThread());
}
//等待 remainMills 的毫秒数,该值最开始是由其他线程传入的,但在多次 wait 的过程中会重新计算
this.wait(remainMills);
//重新计算 remainMills 时间
remainMills = endMills - System.currentTimeMillis();
}
//获得该锁,并从 block 列表中删除当前线程,将 locked 的状态修改为 true 并且指定获得锁的线程就是当前线程
blockedList.remove(Thread.currentThread());
this.locked = true;
this.currentThread = Thread.currentThread();
}
}
}
@Override
public void unlock() {
synchronized (this) {
//判断当前线程是否为获取锁的那个线程,只有加了锁的线程才有资格解锁。
if (currentThread == Thread.currentThread()) {
//将锁的 locked 状态修改为 false
this.locked = false;
Optional.of(Thread.currentThread().getName() + " release the lock.").ifPresent(System.out::println);
//通知其他在线程休息室的线程,可以尝试重新获取锁了
this.notifyAll();
}
}
}
@Override
public List<Thread> getBlockedThreads() {
return Collections.unmodifiableList(blockedList);
}
}