自定义显示锁
synchronized问题
被synchronized同步的线程,是不能被打断,一个线程一旦抢到synchronized同步锁,只要他不释放这个锁
其他线程就只能阻塞,调用者也无法打断这些在等待抢锁的线程
接下来将分两个笔记来解决这个问题:
- 如果自定义一个锁开始实现synchronized的功能
- 如何打断,并且设置超时时间,超时之后就不再进行抢锁
接口
import java.util.Collection;
public interface Lock {
/**
* 加锁,这个和synchronized的区别就是:
*
* @throws InterruptedException
*/
void lock() throws InterruptedException;
/**
* 加锁,指定允许等待时间,如果超时,还没有获得锁,则就抛出TimeOutException
* @param mills
* @throws InterruptedException
* @throws TimeOutException
*/
void lock(long mills) throws InterruptedException,TimeOutException;
/**
* 释放锁
*/
void unLock();
/**
* 返回此时被该锁阻塞的线程
* @return
*/
Collection<Thread> getBlockThread();
int getBlockSize();
class TimeOutException extends Exception{
public TimeOutException(String message) {
super(message);
}
}
}
实现类
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@Slf4j
public class BooleanLock implements Lock{
private Collection<Thread> blockThread = new ArrayList<>();
/**
* 默认值为True,尚未有其他线程持有该锁,能加锁,
* 否则:该锁已经已经其他线程持有,不能加锁
*/
private Boolean enableLock = Boolean.TRUE;
@Override
public synchronized void lock() throws InterruptedException {
while(!enableLock){
// 表示锁已经被其他线程拿走,进入等待,
blockThread.add(Thread.currentThread());
this.wait();
}
// 表示锁已经被释放,可以获取锁
// 当前线程拿到锁可以执行,从blockThread移除
blockThread.remove(Thread.currentThread());
// enableLock set false,其他线程不可再持有该锁
this.enableLock = Boolean.FALSE;
}
@Override
public void lock(long mills) throws InterruptedException, TimeOutException {
}
@Override
public synchronized void unLock() {
// enableLock set true, 当前线程释放了锁,其他线程可进行抢夺该锁
this.enableLock = Boolean.TRUE;
log.info("{} release lock",Thread.currentThread().getName());
// 别忘记唤起其他线程,进入runnable状态
this.notifyAll();
}
@Override
public Collection<Thread> getBlockThread() {
// 设置返回的当前被锁住的线程是不能修改的,防止调用者修改该集合
return Collections.unmodifiableCollection(blockThread);
}
@Override
public int getBlockSize() {
return blockThread.size();
}
}
测试
**import lombok.extern.slf4j.Slf4j;
import java.util.stream.Stream;
@Slf4j
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final Lock lock = new BooleanLock();
// 创建四个线程
Stream.of("T1","T2","T3","T4").forEach(t->{
new Thread(t){
@Override
public void run() {
try {
lock.lock();
log.info("{} have the lock", Thread.currentThread().getName());
excute();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unLock();
}
}
}.start();
});
}
/**
* 模拟线程要执行的任务
*/
private static void excute() throws InterruptedException {
log.info("{} is excute start.....", Thread.currentThread().getName());
Thread.sleep(10_000);
log.info("{} is excute finsh.....", Thread.currentThread().getName());
}
}
运行结果
[T1] INFO study.wyy.concurrency.thread.lock.LockTest - T1 have the lock
[T1] INFO study.wyy.concurrency.thread.lock.LockTest - T1 is excute .....
[T1] INFO study.wyy.concurrency.thread.lock.BooleanLock - T1 release lock
[T4] INFO study.wyy.concurrency.thread.lock.LockTest - T4 have the lock
[T4] INFO study.wyy.concurrency.thread.lock.LockTest - T4 is excute .....
[T4] INFO study.wyy.concurrency.thread.lock.BooleanLock - T4 release lock
[T2] INFO study.wyy.concurrency.thread.lock.LockTest - T2 have the lock
[T2] INFO study.wyy.concurrency.thread.lock.LockTest - T2 is excute .....
[T2] INFO study.wyy.concurrency.thread.lock.BooleanLock - T2 release lock
[T3] INFO study.wyy.concurrency.thread.lock.LockTest - T3 have the lock
[T3] INFO study.wyy.concurrency.thread.lock.LockTest - T3 is excute .....
[T3] INFO study.wyy.concurrency.thread.lock.BooleanLock - T3 release lock
可见只有线程释放了锁了,其他的线程才能抢到锁,执行
问题:
既然是API,就要限制用户的非法操作,比如,我们在main方法中释放锁
@Slf4j
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final Lock lock = new BooleanLock();
// 创建四个线程
Stream.of("T1","T2","T3","T4").forEach(t->{
new Thread(t){
@Override
public void run() {
try {
lock.lock();
log.info("{} have the lock", Thread.currentThread().getName());
excute();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unLock();
}
}
}.start();
});
Thread.sleep(100);
lock.unLock();
}
/**
* 模拟线程要执行的任务
*/
private static void excute() throws InterruptedException {
log.info("{} is excute start.....", Thread.currentThread().getName());
Thread.sleep(10_000);
log.info("{} is excute finsh.....", Thread.currentThread().getName());
}
}
日志
[T1] INFO study.wyy.concurrency.thread.lock.LockTest - T1 have the lock
[T1] INFO study.wyy.concurrency.thread.lock.LockTest - T1 is excute start.....
[main] INFO study.wyy.concurrency.thread.lock.BooleanLock - main release lock
[T4] INFO study.wyy.concurrency.thread.lock.LockTest - T4 have the lock
[T4] INFO study.wyy.concurrency.thread.lock.LockTest - T4 is excute start.....
这个时候就会出现一个问题,当T1抢到锁执行了100ms的时候,main线程把锁给释放了,但是t1还没有执行完成,
锁锁就main线程释放了,导致其他的线程就可以抢锁运行,导致本来期望串行执行的变成了并发执行
解决问题
思路:
- 记录是哪个线程获取到了锁
- 释放的时候进行判断,由这个线程来释放锁
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@Slf4j
public class BooleanLock implements Lock {
private Collection<Thread> blockThread = new ArrayList<>();
/**
* 默认值为True,尚未有其他线程持有该锁,能加锁,
* 否则:该锁已经已经其他线程持有,不能加锁
*/
private Boolean enableLock = Boolean.TRUE;
private Thread currentThread;
@Override
public synchronized void lock() throws InterruptedException {
while (!enableLock) {
// 表示锁已经被其他线程拿走,进入等待,
blockThread.add(Thread.currentThread());
this.wait();
}
// 表示锁已经被释放,可以获取锁
// 当前线程拿到锁可以执行,从blockThread移除
blockThread.remove(Thread.currentThread());
// enableLock set false,其他线程不可再持有该锁
this.enableLock = Boolean.FALSE;
// 记录抢到锁的线程
currentThread = Thread.currentThread();
}
@Override
public void lock(long mills) throws InterruptedException, TimeOutException {
}
@Override
public synchronized void unLock() {
// 判断是不是抢到锁的线程,只有是持有该锁的线程才可以释放该锁
if (currentThread != null && currentThread == Thread.currentThread()){
// enableLock set true, 当前线程释放了锁,其他线程可进行抢夺该锁
this.enableLock = Boolean.TRUE;
log.info("{} release lock", Thread.currentThread().getName());
// 别忘记唤起其他线程,进入runnable状态
this.notifyAll();
}
}
@Override
public Collection<Thread> getBlockThread() {
// 设置返回的当前被锁住的线程是不能修改的,防止调用者修改该集合
return Collections.unmodifiableCollection(blockThread);
}
@Override
public int getBlockSize() {
return blockThread.size();
}
}