首先明确一次显示锁Lock需要的功能:
- 线程加锁;
- 线程超时加锁,超时报超时异常;
- 线程解锁;
- 获取阻塞的线程集合;
- 获取阻塞的线程个数。
根据以上需求定义Lock接口:
public interface Lock {
/**
* 超时异常
*/
class TimeoutException extends Exception {
public TimeoutException(String message) {
super(message);
}
}
void lock() throws InterruptedException;
void lock(long mills) throws InterruptedException, TimeoutException;
void unlock();
/**
* 获取阻塞的线程集合
*/
Collection<Thread> getBlockedThread();
/**
* 获取阻塞的线程个数
*/
int getBlockedSize();
}
定义一个保存线程的队列和一个当前锁状态的标志:
/**
* initValue=true:表示锁已经被某线程占用
* initValue=false:表示锁没有被某线程占用,其它线程可以争抢
*/
private boolean initValue;
private Collection<Thread> blockedThreadCollection = new ArrayList<>();
加锁功能的实现:
首先判断锁的状态,如果可以加锁,从阻塞队列中移除当前线程,将锁的状态置为true,如果锁已经被其它线程还在占用,则将当前线程加入到阻塞队列,并且调用wait方法阻塞当前线程。
@Override
public synchronized void lock() throws InterruptedException {
while (initValue) {
// 如果锁已经被占用,就将当前线程加入到阻塞队列,进入等待状态
blockedThreadCollection.add(Thread.currentThread());
wait();
}
// 如果获取到锁,从阻塞队列中移除当前线程,锁标志置为true
blockedThreadCollection.remove(Thread.currentThread());
Optional.of(Thread.currentThread().getName() + " get the lock monitor.").ifPresent(System.out::println);
initValue = true;
// 防止unlock和lock的不是同一个线程
this.currentThread = Thread.currentThread();
}
超时加锁功能的实现:
如果传入的时间不是正数,直接抛出加锁超时异常。如果当前锁已经被其它线程占用,则wait传入的时间。如果抢占到锁,则变更锁的状态,并将当前上锁的线程保存到一个变量中,释放锁的时候需要校验。
@Override
public synchronized void lock(long mills) throws InterruptedException, TimeoutException {
if (mills < 0) {
lock();
}
long hasRemaining = mills;
long endTime = System.currentTimeMillis() + mills;
while (initValue) {
if (hasRemaining <= 0) {
// 已经超时
throw new TimeoutException(Thread.currentThread().getName() + "get lock time out");
}
blockedThreadCollection.add(Thread.currentThread());
this.wait(mills);
hasRemaining = endTime - System.currentTimeMillis();
}
//blockedThreadCollection.remove(Thread.currentThread());
initValue = true;
this.currentThread = Thread.currentThread();
}
释放锁功能的实现:
这里需要注意的是,当锁已经被线程A占有,线程B是不能调用 unlock的,只能被线程A unlock,所以需要定义一个变量currentThread保存当前占有锁的线程,在释放锁的时候进行校验。
// 保存当前持有锁的线程
private Thread currentThread;
@Override
public synchronized void unlock() {
// unlock保护,其它线程不能解锁加锁的线程
if (currentThread == Thread.currentThread()) {
initValue = false;
Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
notifyAll(); // 唤醒其它线程
}
}
功能测试:
public class LockTest {
public static void main(String[] args) throws InterruptedException {
BooleanLock lock = new BooleanLock();
Stream.of("T1", "T2", "T3", "T4")
.forEach(name -> new Thread(() -> {
try {
lock.lock(100L);
work();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Lock.TimeoutException e) {
Optional.of(Thread.currentThread().getName() + " get lcok time out!").ifPresent(System.out::println);
} finally {
lock.unlock();
}
}, name).start());
// 测试其它线程是否能unlock加锁线程
// lock、unlock方法里应该做保护
//Thread.sleep(100);
//lock.unlock();
}
private static void work() throws InterruptedException {
Optional.of(Thread.currentThread().getName() + " is working...").ifPresent(System.out::println);
Thread.sleep(10_000);
}
}
-
加锁测试结果:
加锁测试结果.png -
超时加锁测试结果
超时加锁测试结果.png
网友评论