本文已参与「新人创作礼」活动,一起开启掘金创作之路。
在Java多线程中,我们会遇到多个线程访问同一个资源,产生竞争,如果操作不到会导致死锁的产生,例如在现实中的十字路口,锁就像红路灯指示器,一旦锁坏了,就会导致交通瘫痪。
通过本篇文章读者可以学到以下内容
- 多线程并发
- 锁的使用
- 超时锁
一、编写锁的接口
public interface Lock {
//自定义异常类
public static class TimeOutException extends Exception{
public TimeOutException(String message){
super(message);
}
}
//无超时锁,可以被打断
void lock() throws InterruptedException;
//超时锁,可以被打断
void lock(long molls) throws InterruptedException,TimeOutException;
//解锁
void unlock();
//获取当前等待的线程
Collection<Thread> getBlockedThread();
//获取当前阻塞的线程数目
int getBlockSize();
}
二、实现接口,编写功能
首先介绍几个重要的方法属性
1,Monitor这里选择的是BooleanLock ,使用this.wait()来阻塞,
2,因为是多线程访问,因此使用Collection来存储等待的线程
3,使用while(initValue),通过标志位initValue来判断当前线程是否在工作中,以进行后面的操作
4,所谓超时锁就是设置超时时间,如果超过这读段时间,抛出异常
public class BooleanLock implements Lock{
private boolean initValue;
private Thread currenThread;
public BooleanLock(){
this.initValue = false;
}
private Collection<Thread> blockThreadCollection = new ArrayList<>();
@Override
public synchronized void lock() throws InterruptedException {
while (initValue){
blockThreadCollection.add(Thread.currentThread());
this.wait();
}
//表明此时正在用,别人进来就要锁住
this.initValue = true;
currenThread = Thread.currentThread();
blockThreadCollection.remove(Thread.currentThread());//从集合中删除
}
@Override
public synchronized void lock(long mills) throws InterruptedException, TimeOutException {
if (mills<=0){
lock();
}else {
long hasRemain = mills;
long endTime = System.currentTimeMillis()+mills;
while (initValue){
if (hasRemain<=0)
throw new TimeOutException("Time out");
blockThreadCollection.add(Thread.currentThread());
hasRemain = endTime-System.currentTimeMillis();
}
this.initValue = true;
currenThread = Thread.currentThread();
}
}
@Override
public synchronized void unlock() {
if (currenThread==Thread.currentThread()){
this.initValue = false; //表明锁已经释放
Optional.of(Thread.currentThread().getName()+ " release the lock monitor").ifPresent(System.out::println);
this.notifyAll();
}
}
@Override
public Collection<Thread> getBlockedThread() {
return Collections.unmodifiableCollection(blockThreadCollection);
}
@Override
public int getBlockSize() {
return blockThreadCollection.size();
}
}
三、测试
public class BlockTest {
public static void main(String[] args) throws InterruptedException {
final BooleanLock booleanLock = new BooleanLock();
// 使用Stream流的方式房间四个线程
Stream.of("T1","T2","T3","T4").forEach(name->{
new Thread(()->{
try {
booleanLock.lock(10);
Optional.of(Thread.currentThread().getName()+" have the lock Monitor").ifPresent(System.out::println);
work();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Lock.TimeOutException e) {
Optional.of(Thread.currentThread().getName()+" time out").ifPresent(System.out::println);
} finally {
booleanLock.unlock();
}
},name).start();
});
}
private static void work() throws InterruptedException{
Optional.of(Thread.currentThread().getName()+" is working.....'").ifPresent(System.out::println);
Thread.sleep(40_000);
}
}
结果:
T1 have the lock Monitor
T1 is working.....'
T2 time out
T4 time out
T3 time out
备注: 如果是需要一直等待就调用 lock(),如果是超时要退出来就调用超时lock(long millo)
多线程消费者和生产者模式并发死锁问题解决
- 文中采用Stream流的方式创建多个生产者和消费者
- 利用对象的wait()进行阻塞
- 利用synchronized 的原理
- 创建唯一的Monitor的LOCK对象
- 通过notifyAll()唤醒阻塞,此处不能用notify()
import java.util.stream.Stream;
public class ProduceConsumer {
private int i;
final private Object LOCK = new Object();
private volatile boolean isProduced = false;
public void produce() throws InterruptedException {
synchronized (LOCK) {
if (isProduced) {
LOCK.wait();
} else {
i++;
System.out.println(Thread.currentThread().getName()+" P-----> " + i);
isProduced = true;
LOCK.notifyAll();
}
}
}
public void consumer() throws InterruptedException {
synchronized (LOCK) {
if (isProduced) {
System.out.println(Thread.currentThread().getName()+" C-----> " + i);
isProduced = false;
LOCK.notifyAll();
} else {
LOCK.wait();
}
}
}
public static void main(String[] args) {
ProduceConsumer produceConsumer = new ProduceConsumer();
Stream.of("P1", "P2", "p3").forEach(name -> {
new Thread(() -> {
while (true) {
try {
produceConsumer.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},name).start();
});
Stream.of("C1", "C2", "C3").forEach(name -> {
new Thread(() -> {
while (true) {
try {
produceConsumer.consumer();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},name).start();
});
}
}