美文网首页
使用synchronized和wait实现一个显示锁Lock

使用synchronized和wait实现一个显示锁Lock

作者: herohua | 来源:发表于2020-01-15 10:33 被阅读0次

    首先明确一次显示锁Lock需要的功能:

    1. 线程加锁;
    2. 线程超时加锁,超时报超时异常;
    3. 线程解锁;
    4. 获取阻塞的线程集合;
    5. 获取阻塞的线程个数。
      根据以上需求定义Lock接口:
    public interface Lock {
    
        /**
         * 超时异常
         */
        class TimeoutException extends Exception {
            public TimeoutException(String message) {
                super(message);
            }
        }
    
        void lock() throws InterruptedException;
    
        void lock(long mills) throws InterruptedException, TimeoutException;
    
        void unlock();
    
        /**
         * 获取阻塞的线程集合
         */
        Collection<Thread> getBlockedThread();
    
        /**
         * 获取阻塞的线程个数
         */
        int getBlockedSize();
    }
    

    定义一个保存线程的队列和一个当前锁状态的标志:

    /**
     * initValue=true:表示锁已经被某线程占用
     * initValue=false:表示锁没有被某线程占用,其它线程可以争抢
     */
    private boolean initValue;
    
    private Collection<Thread> blockedThreadCollection = new ArrayList<>();
    

    加锁功能的实现:

    首先判断锁的状态,如果可以加锁,从阻塞队列中移除当前线程,将锁的状态置为true,如果锁已经被其它线程还在占用,则将当前线程加入到阻塞队列,并且调用wait方法阻塞当前线程。

    @Override
    public synchronized void lock() throws InterruptedException {
        while (initValue) {
            // 如果锁已经被占用,就将当前线程加入到阻塞队列,进入等待状态
            blockedThreadCollection.add(Thread.currentThread());
            wait();
        }
        // 如果获取到锁,从阻塞队列中移除当前线程,锁标志置为true
        blockedThreadCollection.remove(Thread.currentThread());
        Optional.of(Thread.currentThread().getName() + " get the lock monitor.").ifPresent(System.out::println);
        initValue = true;
        // 防止unlock和lock的不是同一个线程
        this.currentThread = Thread.currentThread();
    }
    

    超时加锁功能的实现:

    如果传入的时间不是正数,直接抛出加锁超时异常。如果当前锁已经被其它线程占用,则wait传入的时间。如果抢占到锁,则变更锁的状态,并将当前上锁的线程保存到一个变量中,释放锁的时候需要校验。

    @Override
    public synchronized void lock(long mills) throws InterruptedException, TimeoutException {
        if (mills < 0) {
            lock();
        }
        long hasRemaining = mills;
        long endTime = System.currentTimeMillis() + mills;
        while (initValue) {
            if (hasRemaining <= 0) {
                // 已经超时
                throw new TimeoutException(Thread.currentThread().getName() + "get lock time out");
            }
            blockedThreadCollection.add(Thread.currentThread());
            this.wait(mills);
            hasRemaining = endTime - System.currentTimeMillis();
        }
        //blockedThreadCollection.remove(Thread.currentThread());
        initValue = true;
        this.currentThread = Thread.currentThread();
    }
    

    释放锁功能的实现:

    这里需要注意的是,当锁已经被线程A占有,线程B是不能调用 unlock的,只能被线程A unlock,所以需要定义一个变量currentThread保存当前占有锁的线程,在释放锁的时候进行校验。

    // 保存当前持有锁的线程
    private Thread currentThread;
    
    @Override
    public synchronized void unlock() {
        // unlock保护,其它线程不能解锁加锁的线程
        if (currentThread == Thread.currentThread()) {
            initValue = false;
            Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
            notifyAll();    // 唤醒其它线程
        }
    } 
    

    功能测试:

    public class LockTest {
    
        public static void main(String[] args) throws InterruptedException {
            BooleanLock lock = new BooleanLock();
    
            Stream.of("T1", "T2", "T3", "T4")
                    .forEach(name -> new Thread(() -> {
                        try {
                            lock.lock(100L);
                            work();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (Lock.TimeoutException e) {
                            Optional.of(Thread.currentThread().getName() + " get lcok time out!").ifPresent(System.out::println);
                        } finally {
                            lock.unlock();
                        }
    
                    }, name).start());
    
            // 测试其它线程是否能unlock加锁线程
            // lock、unlock方法里应该做保护
            //Thread.sleep(100);
            //lock.unlock();
    
        }
    
        private static void work() throws InterruptedException {
            Optional.of(Thread.currentThread().getName() + " is working...").ifPresent(System.out::println);
            Thread.sleep(10_000);
        }
    }
    
    1. 加锁测试结果:


      加锁测试结果.png
    2. 超时加锁测试结果


      超时加锁测试结果.png

    相关文章

      网友评论

          本文标题:使用synchronized和wait实现一个显示锁Lock

          本文链接:https://www.haomeiwen.com/subject/yuooactx.html