Java优雅的实现锁和超时锁

231 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

在Java多线程中,我们会遇到多个线程访问同一个资源,产生竞争,如果操作不到会导致死锁的产生,例如在现实中的十字路口,锁就像红路灯指示器,一旦锁坏了,就会导致交通瘫痪。

通过本篇文章读者可以学到以下内容

  1. 多线程并发
  2. 锁的使用
  3. 超时锁

一、编写锁的接口

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)

多线程消费者和生产者模式并发死锁问题解决

  1. 文中采用Stream流的方式创建多个生产者和消费者
  2. 利用对象的wait()进行阻塞
  3. 利用synchronized 的原理
  4. 创建唯一的Monitor的LOCK对象
  5. 通过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();
        });
        
    }
}