LOCK
之前的文章说过,synchronized的关键字存在一些缺陷,那么有没有办法可以解决呢?
我们从缺陷下手:无法控制阻塞时长、阻塞不可中断。我们先手写一个lock锁,然后再去看ReentrantLock的实现。
Lock 接口类
import java.util.List;
import java.util.concurrent.TimeoutException;
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> getWaitThreadLists();
}
实现Lock类,定义一个BooleanLock
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;
public class BooleanLock implements Lock {
private boolean locked;
private Thread currentThread;
private final List<Thread> waitThreadLists = new ArrayList<>();
public BooleanLock() {
}
public BooleanLock(boolean locked) {
this.locked = locked;
}
@Override
public void lock() throws InterruptedException {
synchronized (this) {
Thread thread = Thread.currentThread();
while (locked) {
try {
if (!waitThreadLists.contains(thread)) {
waitThreadLists.add(thread);
}
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
waitThreadLists.remove(thread);
throw e;
}
}
System.out.println(thread.getName() + "获取锁成功,继续执行任务");
locked = true;
waitThreadLists.remove(thread);
this.currentThread = thread;
}
}
@Override
public void lock(long mills) throws InterruptedException, TimeoutException {
synchronized (this) {
if (mills < 0) {
this.lock();
}
long remainingMills = mills;
long endMills = System.currentTimeMillis() + remainingMills;
Thread thread = Thread.currentThread();
while (locked) {
//如果有线程已经获取的lock,当前线程循环等待计算过期时间是否到达。
if (remainingMills <= 0) {
throw new TimeoutException();
}
try {
if (!waitThreadLists.contains(thread)) {
waitThreadLists.add(thread);
}
this.wait(remainingMills);
} catch (InterruptedException e) {
//防止进入阻塞时被中断,导致队列的线程无法回收。
waitThreadLists.remove(thread);
throw e;
}
remainingMills = endMills - System.currentTimeMillis();
}
//当locked =false ,表示当前线程可以进行拿锁处理
locked = true;
waitThreadLists.remove(thread);
this.currentThread = thread;
}
}
@Override
public void unLock() {
Thread thread = Thread.currentThread();
synchronized (this) {
if (currentThread == thread) {
System.out.println("当前线程:" + thread.getName() + "释放锁通知其他线程");
this.locked = false;
this.notifyAll();
}
}
}
@Override
public List<Thread> getWaitThreadLists() {
return Collections.unmodifiableList(waitThreadLists);
}
}
测试类
模拟业务场景调用Lock方法的两种方式
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class LockTest {
private final Lock lock = new BooleanLock();
public void syncMethod() {
String name = Thread.currentThread().getName();
try {
this.lock.lock();
int i = new Random().nextInt(10);
//模拟处理任务耗时
TimeUnit.SECONDS.sleep(i);
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
} finally {
lock.unLock();
}
}
public void syncMethodTimeOut() {
try {
lock.lock(1000);
System.out.println("开始处理任务");
int i = new Random().nextInt(10);
//模拟处理任务耗时
TimeUnit.SECONDS.sleep(i);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unLock();
}
}
}
测试1:不带超时时间的Lock方法。
public static void main(String[] args) {
LockTest lockTest = new LockTest();
IntStream.range(0, 10)
.mapToObj(i -> new Thread(lockTest::syncMethod))
.forEach(Thread::start);
}
测试结果
测试2: 测试中断线程时的等待队列数据是否清空
public static void main(String[] args) throws InterruptedException {
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(lockTest::syncMethod,"thread-1");
thread1.start();
TimeUnit.MILLISECONDS.sleep(100);
Thread thread2 = new Thread(lockTest::syncMethod,"thread-2");
thread2.start();
TimeUnit.MILLISECONDS.sleep(200);
thread2.interrupt();
//测试中断线程后,lock对象的等待队列释放清空
TimeUnit.MILLISECONDS.sleep(300);
List<Thread> waitThreadLists = lockTest.lock.getWaitThreadLists();
System.out.println(waitThreadLists);
}
测试结果
测试3:使用带超时时间的lock方法
public static void main(String[] args) throws InterruptedException {
LockTest lockTest = new LockTest();
Thread thread1 = new Thread(lockTest::syncMethodTimeOut, "thread-1");
thread1.start();
TimeUnit.MILLISECONDS.sleep(100);
Thread thread2 = new Thread(lockTest::syncMethodTimeOut, "thread-2");
thread2.start();
TimeUnit.MILLISECONDS.sleep(200);
//测试中断线程后,lock对象的等待队列释放清空
TimeUnit.MILLISECONDS.sleep(300);
List<Thread> waitThreadLists = lockTest.lock.getWaitThreadLists();
System.out.println(waitThreadLists);
}
测试结果