一、用boolean自定义锁
BooleanLock的功能:
- (1) 多个线程通过lock()方法争抢锁
- (2) 可中断被阻塞的线程
- (3) 阻塞的线程可超时
Lock.java接口
package p96_boolean_lock;
import java.util.List;
import java.util.concurrent.TimeoutException;
/**
* Lock接口
*/
public interface Lock {
/**
* lock()方法永远阻塞,除非获得了锁,和synchronized类似,但该方法可以被中断,中断时
* 抛出InterruptedException异常
* @throws InterruptedException
*/
void lock() throws InterruptedException;
/**
* lock(long mills)除了可以被中断外,还增加了超时功能
* @param mills
* @throws InterruptedException
* @throws TimeoutException
*/
void lock(long mills) throws InterruptedException, TimeoutException;
/**
* unlock()方法用来进行锁的释放
*/
void unlock();
/**
* 用来获取有哪些线程被阻塞
* @return
*/
List<Thread> getBlockedThreads();
}
BooleanLock.java实现Lock接口
package p96_boolean_lock;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeoutException;
import static java.lang.System.currentTimeMillis;
import static java.lang.Thread.currentThread;
/**
* @ClassName BooleanLock
* @Description TODO
* @Author Cays
* @Date 2019/5/3 16:20
* @Version 1.0
**/
public class BooleanLock implements Lock {
//代表当前拥有锁的线程
private Thread currentThread;
//locked是一个Boolean开关,false代表当前该锁没有被任何线程获得
//或者已经释放,true代表该锁已经被某个线程获得,即currentThead
private boolean locked = false;
/**
* blocked List存放哪些线程在获取当前线程时进入了阻塞状态
*/
private final List<Thread> blockedList = new ArrayList<>();
@Override
public void lock() throws InterruptedException {
// 1. lock()方法使用同步代码块的方式进行同步
synchronized (this){
// 2. 如果当前锁已经被某个线程获得,则该线程将加入阻塞队列,并使当前线程
// wait释放对this monitor的所有权
while (locked){
/**
* 如果某个线程被中断,那么它将有可能还存在于blockList中,修复如下:
*/
//暂存当前线程
final Thread tempThread = currentThread();
try{
if (!blockedList.contains(tempThread)){
blockedList.add(tempThread);
}
this.wait();
}catch (InterruptedException e){
//如果当前线程在wait时被中断,则从blockList中将其移除,避免内存泄漏
blockedList.remove(tempThread);
//继续抛出异常
throw e;
}
}
/**
* 3. 如果当前锁没有被其他线程获得,该线程尝试从阻塞队列删除自己,如果当前线程
* 从未进入过阻塞队列,删除方法不会有任何影响;如果当前线程是从wait set中被唤
* 醒的,则需要从阻塞队列中删除自己
*/
blockedList.remove(currentThread());
// 4. locked开关设为true
this.locked=true;
// 5. 记录获取锁的过程
this.currentThread=currentThread();
}
}
@Override
public void lock(long mills) throws InterruptedException, TimeoutException {
// 1. 如果mills不合法,默认调用lock()方法,也可以抛出异常
synchronized (this) {
if (mills <= 0) {
this.lock();
} else {
System.out.println("enter locked:"+locked);
long remainingMills = mills;
long endMills = currentTimeMillis() + remainingMills;
while (locked) {
//暂存当前线程
final Thread tempThread = currentThread();
try {
// 2. 如果remainingMills <= 0说明当前线程被其他线程唤醒或者在指定时间
// wait没有获得锁,这种情况下抛出异常
if (remainingMills <= 0) {
throw new TimeoutException("can not get lock during " + mills+" ms.");
}
if (!blockedList.contains(tempThread)) {
blockedList.add(tempThread);
}
/**
* 3. 等待remainingMills的毫秒数,最开始由其他线程传入,之后wait
* 会重新计算
*/
this.wait(remainingMills);
// 4. 重新计算remainingMills的时间
remainingMills = endMills - currentTimeMillis();
} catch (InterruptedException | TimeoutException e) {
//如果当前线程在wait时被中断,则从blockList中将其移除,避免内存泄漏
blockedList.remove(tempThread);
//继续抛出异常
throw e;
}
}
/**
* 5. 获得该锁,并且从block的列表中删除当前线程,将locked的状态修改为true并
* 指定获得锁的线程就是当前线程
*/
blockedList.remove(currentThread());
this.locked = true;
System.out.println("currentThread="+currentThread());
this.currentThread = currentThread();
}
}
}
@Override
public void unlock() {
synchronized (this){
/**
* 1. 判断当前线程是否是加锁的线程,只有加了锁的线程才有资格解锁
*/
//System.out.println("currentThread:"+currentThread);
//System.out.println("currentThread:"+currentThread());
if (currentThread == currentThread()){
// 2. 将locked的状态修改为false
this.locked=false;
Optional.of(currentThread().getName()+" release the lock.")
.ifPresent(System.out::println);
// 3. 通知其他在wait set的线程,可以争抢锁
this.notifyAll();
}
}
}
@Override
public List<Thread> getBlockedThreads() {
return Collections.unmodifiableList(blockedList);
}
}
二、多个线程通过lock()方法争抢锁
打开(1)处注释
package p96_boolean_lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;
/**
* @ClassName BooleanLockTest
* @Description TODO
* (1) 多个线程通过lock()方法争抢锁
* (2) 可中断被阻塞的线程
* @Author Cays
* @Date 2019/5/3 16:54
* @Version 1.0
**/
public class BooleanLockTest {
// 1. 定义BooleanLock
private final Lock lock=new BooleanLock();
// 2. 使用try/finally语句确保lock每次都能被正确释放
public void syncMethod(){
try{
//加锁
lock.lock();
int randomInt = 1;
System.out.println(currentThread()+" get the lock.");
//休眠10s
TimeUnit.MILLISECONDS.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public void syncMethodTimeoutable(){
try{
lock.lock(1000);
System.out.println(currentThread()+" get the lock.");
int randomInt = 10; //current().nextInt(10);
//休眠10s
TimeUnit.SECONDS.sleep(randomInt);
}catch (InterruptedException | TimeoutException e){
//System.out.println("超时");
e.printStackTrace();
}finally {
//System.out.println("超时2");
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
BooleanLockTest booleanLockTest=new BooleanLockTest();
// 3. 定义一个线程并启动
//(1) 多个线程通过lock()方法争抢锁
IntStream.range(0,10)
.mapToObj(i->new Thread(booleanLockTest::syncMethod))
.forEach(Thread::start);
/**
* (2) 可中断被阻塞的线程
*/
/*new Thread(booleanLockTest::syncMethod,"T1").start();
TimeUnit.MILLISECONDS.sleep(2);
Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
t2.interrupt();
TimeUnit.MILLISECONDS.sleep(10);*/
/**
* (3) 阻塞的线程可超时
*/
/*new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
TimeUnit.MILLISECONDS.sleep(10);
Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);*/
}
}
只有当前一个线程释放monitor的锁后,后一个线程才能继续执行。
结果:
三、可中断被阻塞的线程
打开(2)处注释
package p96_boolean_lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;
/**
* @ClassName BooleanLockTest
* @Description TODO
* (1) 多个线程通过lock()方法争抢锁
* (2) 可中断被阻塞的线程
* @Author Cays
* @Date 2019/5/3 16:54
* @Version 1.0
**/
public class BooleanLockTest {
// 1. 定义BooleanLock
private final Lock lock=new BooleanLock();
// 2. 使用try/finally语句确保lock每次都能被正确释放
public void syncMethod(){
try{
//加锁
lock.lock();
int randomInt = 10;
System.out.println(currentThread()+" get the lock.");
//休眠10s
TimeUnit.SECONDS.sleep(randomInt);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public void syncMethodTimeoutable(){
try{
lock.lock(1000);
System.out.println(currentThread()+" get the lock.");
int randomInt = 10; //current().nextInt(10);
//休眠10s
TimeUnit.SECONDS.sleep(randomInt);
}catch (InterruptedException | TimeoutException e){
//System.out.println("超时");
e.printStackTrace();
}finally {
//System.out.println("超时2");
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
BooleanLockTest booleanLockTest=new BooleanLockTest();
// 3. 定义一个线程并启动
//(1) 多个线程通过lock()方法争抢锁
/*IntStream.range(0,10)
.mapToObj(i->new Thread(booleanLockTest::syncMethod))
.forEach(Thread::start);*/
/**
* (2) 可中断被阻塞的线程
*/
//new Thread(booleanLockTest::syncMethod,"T1").start();
//TimeUnit.MILLISECONDS.sleep(2);
Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
t2.interrupt();
TimeUnit.MILLISECONDS.sleep(10);
System.out.println("T2 state:"+t2.getState());
System.out.println("T2 interrupt:"+t2.isInterrupted());
/**
* (3) 阻塞的线程可超时
*/
/*new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
TimeUnit.MILLISECONDS.sleep(10);
Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);*/
}
}
T2线程试图休眠10s可是在T2开始10ms时被interrupt方法打断。
结果:
四、阻塞的线程可超时
打开(3)处注释
package p96_boolean_lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;
import static java.lang.Thread.currentThread;
import static java.util.concurrent.ThreadLocalRandom.current;
/**
* @ClassName BooleanLockTest
* @Description TODO
* (1) 多个线程通过lock()方法争抢锁
* (2) 可中断被阻塞的线程
* @Author Cays
* @Date 2019/5/3 16:54
* @Version 1.0
**/
public class BooleanLockTest {
// 1. 定义BooleanLock
private final Lock lock=new BooleanLock();
// 2. 使用try/finally语句确保lock每次都能被正确释放
public void syncMethod(){
try{
//加锁
lock.lock();
int randomInt = 10;
System.out.println(currentThread()+" get the lock.");
//休眠10s
TimeUnit.SECONDS.sleep(randomInt);
}catch (InterruptedException e){
e.printStackTrace();
}finally {
//释放锁
lock.unlock();
}
}
public void syncMethodTimeoutable(){
try{
lock.lock(1000);
System.out.println(currentThread()+" get the lock.");
int randomInt = 10; //current().nextInt(10);
//休眠10s
TimeUnit.SECONDS.sleep(randomInt);
}catch (InterruptedException | TimeoutException e){
//System.out.println("超时");
e.printStackTrace();
}finally {
//System.out.println("超时2");
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
BooleanLockTest booleanLockTest=new BooleanLockTest();
// 3. 定义一个线程并启动
//(1) 多个线程通过lock()方法争抢锁
/*IntStream.range(0,10)
.mapToObj(i->new Thread(booleanLockTest::syncMethod))
.forEach(Thread::start);*/
/**
* (2) 可中断被阻塞的线程
*/
//new Thread(booleanLockTest::syncMethod,"T1").start();
//TimeUnit.MILLISECONDS.sleep(2);
/*Thread t2=new Thread(booleanLockTest::syncMethod,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
t2.interrupt();
TimeUnit.MILLISECONDS.sleep(10);
System.out.println("T2 state:"+t2.getState());
System.out.println("T2 interrupt:"+t2.isInterrupted());*/
/**
* (3) 阻塞的线程可超时
*/
new Thread(booleanLockTest::syncMethodTimeoutable,"T1").start();
TimeUnit.MILLISECONDS.sleep(10);
Thread t2 = new Thread(booleanLockTest::syncMethodTimeoutable,"T2");
t2.start();
TimeUnit.MILLISECONDS.sleep(10);
}
}
T1线程休眠10s期间,线程T2试图获取monitor的锁,一秒后失败,超时,T2结束生命周期;T1线程结束后直接结束。
结果: