一 定义Lock接口
public interface Lock{
void lock()throws InterruptedException;
void lock(long mills)throws InterruptedException,TimeoutException;
List<Thread>getBlockedThreads();
}
上述接口中:
lock( )方法永远阻塞,除非获得了锁,这一点和synchronized非常类似,但是该方法是可以被中断的,中断时会抛出interruptException异常
lock(long mikks )方法除了可以被中断以外,还增加了对应的超时功能
unlock ()方法可以用来进行锁的释放
getBlockedThreads()用于获取当前有哪些线程被阻塞。
二 实现BooleanLock
BooleanLock 是lock的一个boolean实现,通过控制一个boolean 变量的开关来决定是否允许当前线程获取该锁首先定义三个非常重要的成员变量,如下:
public class BooleanLock implements Lock{
private boolean locked;
private Thread currentThread; //当前持有锁的线程
private final List<Thread> blockedThreads = new ArrayList<>(); //等待获取锁的线程
其中 currentThread 代表当前拥有锁的线程,locked 是一个boolean 开关,false 代表当前该锁没有被任何线程获得或者已经释放,true代表该锁已经被某个线程获得,该线程就是currentThread,blockedList用来存储哪些线程在获取当前线程时进入了阻塞状态 ,继续实现lock方法,如下
@Override
public void lock() throws InterruptedException {
synchronized (this) {
while (locked) {
if (!blockedThreads.contains(Thread.currentThread())) {
blockedThreads.add(Thread.currentThread());
}
try {
this.wait();
} catch (InterruptedException e) {
blockedThreads.remove(Thread.currentThread());
throw e;
}
}
locked = true;
currentThread = Thread.currentThread();
blockedThreads.remove(currentThread);
}
}
在上面代码中:
1 lock使用同步代码块方式进行方法同步,
2 如果当前锁已经被某个线程获得,则该线程将加入阻塞队列,并且使当前线程wait释放对this monitor的所有权,,
3如果当前锁没有被其他线程获得,则该线程将尝试从阻塞队列中删除自己(注意:如果当前线程从未进入过阻塞队列,删除方法不会有任何影响,如果当前线程是从wait set中被唤醒的,则需要从阻塞队列中将自己删除)
4 lock 开关被指定为true
5 记录获得锁的线程
(2)实现一个 unLock()的方法:
@Override
public void unLock() {
synchronized (this) {
if (currentThread == Thread.currentThread()) {
locked = false;
this.notifyAll();
}
}
}
解锁唤醒所有线程继续争抢机会:
测试一下:
public class TestMyLock {
private Random random = new Random();
private MyLock myLock = new BooleanLock();
public void syncMethod() {
try {
myLock.lock();
int rand = random.nextInt(10);
System.out.println(Thread.currentThread().getName() + " hold lock, and will sleep " + rand + " seconds");
TimeUnit.SECONDS.sleep(rand);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (myLock.isHeldByCurrentThread()) {
myLock.unLock();
System.out.println(Thread.currentThread().getName() + " release lock");
}
}
}
static void testSync() {
final TestMyLock testMyLock = new TestMyLock();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(() -> {
testMyLock.syncMethod();
}, i + "");
thread.start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(testMyLock.myLock.getBlockedThreads());
}
public static void main(String[] args) throws InterruptedException {
testSync();
}
}
结果如下:
看到控制台有条不紊的打印出加锁和解锁的操作,说明MyLock实现了线程同步的功能。
实现一个限时的锁获取
@Override
public void lock(long timeMillis) throws InterruptedException, TimeoutException {
synchronized (this) {
if (timeMillis <= 0) {
this.lock();
} else {
long remainMills = timeMillis;
long endTime = System.currentTimeMillis() + remainMills; //截至时刻
while (locked) {
if (remainMills <= 0) { //剩余等待时间
throw new TimeoutException("cannot get lock during " + remainMills);
}
if (!blockedThreads.contains(Thread.currentThread())) {
blockedThreads.add(Thread.currentThread());
}
this.wait(remainMills); //等待一定时间
remainMills = endTime - System.currentTimeMillis(); //重新计算等待时间:当前时间点与while执行之前的时间点之差。
}
locked = true;
currentThread = Thread.currentThread();
blockedThreads.remove(currentThread);
}
}
}
测试一下:、
mian 方法:
static void testTimeOut() throws InterruptedException {
TestMyLock blt = new TestMyLock();
new Thread(blt::syncMethod, "Tl").start();
TimeUnit.MILLISECONDS.sleep(2);
Thread t2 = new Thread(blt::syncMethodOut, "T2");
t2.start();
}
public void syncMethodOut() {
try {
myLock.lock(1000);
int rand = random.nextInt(10);
System.out.println(Thread.currentThread().getName() + " hold lock, and will sleep " + rand + " seconds");
TimeUnit.SECONDS.sleep(rand);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
} finally {
if (myLock.isHeldByCurrentThread()) {
myLock.unLock();
System.out.println(Thread.currentThread().getName() + " release lock");
}
}
}
输出如下:
可以看出当线程T1已经获取锁,并且睡眠8s,线程T2尝试在1s内获取锁,结果失败。