【详解】Java多线程之自定义Boolean锁&捕获线程中的异常
一、 自定义Boolean锁
需求:
- 设置一个锁,让只有一个线程可以工作,其他的线程阻塞
- 可以查看
都有哪些线程阻塞
- 只用加锁的线程才可以释放锁,其他线程不允许修改
先定义一个接口,定义锁的规范
public interface Lock {
void lock() throws InterruptedException;
void unlock();
Collection<Thread> getBlockedThread();
int getBolckedSize();
}
定义借口的实现类,实现相关需求
public class BooleanLock implements Lock {
private boolean initValue;//false时为该锁被释放,true时代表被占有
private Thread currentThread;
private Collection<Thread> blockedThreadCollection = new ArrayList<>();
public BooleanLock() {
this.initValue = false;
}
@Override
public synchronized void lock() throws InterruptedException {//抢锁
while (initValue) {//此时如果没有抢到锁
blockedThreadCollection.add(Thread.currentThread());
this.wait();
}
blockedThreadCollection.remove(Thread.currentThread());
this.initValue = true;
this.currentThread = Thread.currentThread();//保存抢到锁的线程
}
@Override
public synchronized void unlock() {
if (currentThread == Thread.currentThread()) {//防止其他线程把正在执行的线程锁释放
this.initValue = false;
System.out.println(Thread.currentThread().getName() + "释放");
this.notifyAll();
}else {
System.out.println("您没有释放的权限!!!");
}
}
@Override
public Collection<Thread> getBlockedThread() {
return Collections.unmodifiableCollection(blockedThreadCollection);
}
@Override
public int getBolckedSize() {
return blockedThreadCollection.size();
}
}
实现方式
- 通过synchronized保护一个Boolean的变量,如果为false,表示这个锁还有没占用
- 如果为true,
线程陷入休眠,将该线程加入到阻塞队列
- 当线程执行完毕,
将Boolean的变量至为false
,表示释放这个锁,同时唤醒其他阻塞线程
- 如果线程抢到了锁,则退出阻塞队列,并且在
锁中定义一个属性,保存当前抢到锁的线程是哪个
- 在释放锁的时候,
检查是否是抢到锁的线程想要释放该锁
,如果不是,拒绝释放
测试
public class LockTest {
public static void main(String[] args) throws InterruptedException {
final BooleanLock booleanLock = new BooleanLock();
Stream.of("T1","T2","T3","T4","T5")
.forEach(name->
new Thread(()->{
try {
booleanLock.lock();
System.out.println(Thread.currentThread().getName() + "抢到了锁~");
work();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
booleanLock.unlock();
}
},name).start()
);
Thread.sleep(100);
booleanLock.unlock();
}
private static void work(){
System.out.println(Thread.currentThread().getName() + "正在工作....");
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果:
T1抢到了锁~
T1正在工作…
您没有释放的权限!!!
T1释放
T5抢到了锁~
T5正在工作…
T5释放
T2抢到了锁~
T2正在工作…
T2释放
T4抢到了锁~
T4正在工作…
T4释放
T3抢到了锁~
T3正在工作…
T3释放
分析:
- 可以发现只有一个线程在工作,每当其释放锁,其他的线程才能继续工作
- 当主线程想要释放锁时,被提示没有权限
二、 捕获线程中的异常
需求
- 由于无论是覆写run方法或者还是传递一个Runnable接口实例,都无法捕获线程中出现的异常
- Thread提供了一个实例方法:
setUncaughtExceptionHandler
,可以由开发者完全的控制所有没有捕获到的异常
public class ThreadException {
private static int A = 10;
private static int B = 0;
public static void main(String[] args) {
Thread t = new Thread(()->{
try {
Thread.sleep(5000);
int result = A/B;
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.setUncaughtExceptionHandler((Thread,e)->{//处理未捕获的异常
System.out.println(e);//打印异常
System.out.println(Thread.getName());//打印出现异常的线程名称
});
t.start();
}
}
结果
java.lang.ArithmeticException: / by zero
Thread-0
若想深入学习,参考下面的博客