zoukankan      html  css  js  c++  java
  • java线程基础巩固---如何实现一个自己的显式锁Lock

    拋出synchronized问题:

    对于一个方法上了同锁如果被一个线程占有了,而假如该线程长时间工作,那其它线程不就只能傻傻的等着,而且是无限的等这线程工作完成了才能执行自己的任务,这里来演示一下这种场景:

    上面代码就是开启了两个线程,执行顺序有先后,分别执行这个同步方法,看下结果:

    对于线程的中止其实是有系统API的,咱们可以试验一上试图打断t2线程不让他傻傻的一直等着了:

    看结果:

    因为同步代码块中是一个while的死循环,接收不到中断异常的,所以基于这个场景咱们需要自己来定义一个带有超时功能的锁,下面开始。

    不包括超时的Lock:

    其实在java5的并发包中有专门的Lock,这里校仿一下系统的实现,先新建一个接口,将其锁的行为给定义一下:

    然后新建一个具体类来实现该接口:

    下面先来实现lock()方法,这里先定义两个成员变量:

    首先判断是否锁被其它线程给占用了,如果占用了将当前线程加入阻塞集合中:

    但!!!目前代码是有问题的,根据之前所学对于wait()是一定得要有一个monitor才行,否则肯定会报错,所以:

    当这个循环条件退出了那证明就拿到了锁,所以接着处理正常的情况:

    接下来实现unlock方法,比较容易理解直接贴出代码:

    然后再将阻塞的查询方法给实现:

    但是此次有一个细节问题:就是getBlockedThreads方法是直接将这个集合给返回给调用者了,此时调用者是可以修改Lock里面的blockedThreadCollection对象的,所以目前这样直接返回不太安全,应该包装一下不能让调用者来修改它,如下:

    看一下官方的说明:

    好,接下来使用一下咱们自己写的Lock,如下:

    编译运行:

    呃,抛异常了,在unlock()方法中的notifyAll(),它跟wait()方法一样也是需要一个monitor,所以修改代码如下:

    再次运行:

    这样咱们自己就定义了一个跟syncronized关键字类似的效果,只有一个线程执行完了其它线程才有机会去工作,不过目前的Lock还有一个问题,就是调用者可以随意的调用咱们封装的unlock()方法,而造成的影响嘛,下面做实验来说明一下:

    看结果:

    所以~~拉下来解决这个问题,其实解决也很简单,在Lock中加入一个变量用来记录lock()的线程,然后在unlock()时做一个判断,具体做法如下:

    /**
     * 以boolean来实现锁
     */
    public class BooleanLock implements Lock {
    
        //如果为true代表已经被其它线程抢到锁了;否则代表锁是空闲的还木有被其它线程占有
        private boolean initValue;
        //当前拿到锁的线程,以便用来控制释放锁状态问题
        private Thread currentThread;
    
        //阻塞的线程集合
        private Collection<Thread> blockedThreadCollection = new ArrayList<>();
    
        @Override
        public synchronized void lock() throws InterruptedException {
            while (initValue) {//说明锁已经被其它线程占用了,此时让线程进入等待状态
                blockedThreadCollection.add(Thread.currentThread());
                this.wait();
            }
            blockedThreadCollection.remove(Thread.currentThread());
            this.initValue = true;
            currentThread = Thread.currentThread();
        }
    
        @Override
        public void lock(long timeOutMills) throws InterruptedException, TimeOutException {
    
        }
    
        @Override
        public synchronized void unlock() {
            if (Thread.currentThread() == currentThread) {
                this.initValue = false;
                Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
                this.notifyAll();
            }
        }
    
        @Override
        public Collection<Thread> getBlockedThreads() {
            return Collections.unmodifiableCollection(blockedThreadCollection);
        }
    
        @Override
        public int getBlockedSize() {
            return blockedThreadCollection.size();
        }
    }

    编译运行:

    就成功修复了这个BUG。

    超时的Lock:

    好,如文篇描述开头所抛出的synchronized所存在问题,这里通过咱们自己的写的锁来解决一下,当然此时就需要实现Lock中最后一个木有实现的方法了,如下:

    那如何实现呢?其逻辑也比较简单,这里就不多解释了,直接贴代码:

    /**
     * 以boolean来实现锁
     */
    public class BooleanLock implements Lock {
    
        //如果为true代表已经被其它线程抢到锁了;否则代表锁是空闲的还木有被其它线程占有
        private boolean initValue;
        //当前拿到锁的线程,以便用来控制释放锁状态问题
        private Thread currentThread;
    
        //阻塞的线程集合
        private Collection<Thread> blockedThreadCollection = new ArrayList<>();
    
        @Override
        public synchronized void lock() throws InterruptedException {
            while (initValue) {//说明锁已经被其它线程占用了,此时让线程进入等待状态
                blockedThreadCollection.add(Thread.currentThread());
                this.wait();
            }
            blockedThreadCollection.remove(Thread.currentThread());
            this.initValue = true;
            currentThread = Thread.currentThread();
        }
    
        @Override
        public synchronized void lock(long timeOutMills) throws InterruptedException, TimeOutException {
            if (timeOutMills <= 0)//先做一个容错处理
                lock();
            long hasRemaining = timeOutMills;
            long endTime = System.currentTimeMillis() + timeOutMills;
            while (initValue) {
                if (hasRemaining <= 0)
                    throw new TimeOutException("Time out");
                blockedThreadCollection.add(Thread.currentThread());
                this.wait(timeOutMills);//此时需要调用带参数的wait()方法啦
                hasRemaining = endTime - System.currentTimeMillis();
            }
            blockedThreadCollection.remove(Thread.currentThread());
            this.initValue = true;
            currentThread = Thread.currentThread();
        }
    
        @Override
        public synchronized void unlock() {
            if (Thread.currentThread() == currentThread) {
                this.initValue = false;
                Optional.of(Thread.currentThread().getName() + " release the lock monitor.").ifPresent(System.out::println);
                this.notifyAll();
            }
        }
    
        @Override
        public Collection<Thread> getBlockedThreads() {
            return Collections.unmodifiableCollection(blockedThreadCollection);
        }
    
        @Override
        public int getBlockedSize() {
            return blockedThreadCollection.size();
        }
    }

    接着来改一下测试代码:

    编译运行:

  • 相关阅读:
    扩展springMVC
    SpringBoot推荐的Thymeleaf
    springboot使用注解添加组件
    @PropertySource 加载指定的配置文件
    读取yml配置文件中的值
    媒体查询
    若是前台接收的数据为null的不进行传递
    DATA时间:若是数据库存储的为毫秒,而前台需要的是秒,下面介绍一个工具类:
    表单验证
    Redis集群总结
  • 原文地址:https://www.cnblogs.com/webor2006/p/9058369.html
Copyright © 2011-2022 走看看