• Java并发编程原理与实战十五:手动实现一个可重入锁


     package com.roocon.thread.ta1;
    
    public class Sequence {
    
        private MyLock lock = new MyLock();
    
        private int value;
    
        public int getNext() {
            lock.lock();
            value++;
            lock.unlock();
            return value;
    
        }
    
        public static void main(String[] args) {
    
            Sequence s = new Sequence();
    
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while(true)
                        System.out.println(s.getNext());
                }
            }).start();
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while(true)
                        System.out.println(s.getNext());
                }
            }).start();
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while(true)
                        System.out.println(s.getNext());
                }
            }).start();
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while(true)
                        System.out.println(s.getNext());
                }
            }).start();
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    while(true)
                        System.out.println(s.getNext());
                }
            }).start();
        }
    
    }
     
    
    
     
    package com.roocon.thread.ta1;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    public class MyLock implements Lock {
    
        private boolean isLocked = false;
    
        @Override
        public synchronized void lock() {
           
            while (isLocked) {//如果不是第一个进来的线程,就需要等待
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            isLocked = true;//第一个进来的线程获得锁,不需要等待
    
        }
    
        @Override
        public synchronized void unlock() {
            isLocked = false;
            notify();//wait notify 必须和synchronized一起使用
        }
    
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        @Override
        public boolean tryLock() {
            return false;
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    }
     

    运行结果:

     
    1
    2
    3
    4
    5
    6
    7
    ...
     

    现在来模拟下,基于以上代码,锁是否可重入:

     
    package com.roocon.thread.ta1;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo {
        MyLock lock = new MyLock();
    
        public void a() {
            lock.lock();
            System.out.println("a");
            b();
            lock.unlock();
        }
    
        public void b() {
            lock.lock();
            System.out.println("b");
            lock.unlock();
        }
    
        public static void main(String[] args) {
            Demo d = new Demo();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    d.a(); //输出a,并且一直处于等待状态,程序并未运行结束
                }
            }).start();
        }
    }
     

    分析以上运行结果:

    线程1调用a方法,第一次进入lock方法,去获取锁。此时,isLocked为false,于是将标志锁改为true。然后,输出a。再去执行b方法。此时,再次去调用lock方法。lock方法是使用

    synchronized修饰的,是可重入的,于是继续执行b方法中的代码。判断isLocked,由于之前进入拿到了锁,因此isLocked为true,于是,会一直等待等待。这就是为什么输出a一直等待的原因。

    为了实现可重入锁的效果,改进代码如下:

     
    package com.roocon.thread.ta1;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    public class MyLock implements Lock {
    
        private boolean isLocked = false;
        private Thread lockBy = null;
        private int lockCount = 0;
    
        @Override
        public synchronized void lock() {
            if (isLocked && lockBy != Thread.currentThread()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            isLocked = true;
            lockBy = Thread.currentThread();
            lockCount++;
    
        }
    
        @Override
        public synchronized void unlock() {
            if (lockBy == Thread.currentThread()) {
                lockCount--;
                if (lockCount == 0) {
                    isLocked = false;
                    notify();//wait notify 必须和synchronized一起使用
                }
            }
    
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        @Override
        public boolean tryLock() {
            return false;
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    }
     

    运行结果:

    a
    b

    解释以上运行结果:

    线程1调用a方法,执行lock方法。线程1第一次进入,获得锁,于是,将isLocked设置为true,且lockBy为当前线程Thread1,同时,lockCount=1。

    输出a后,再次执行代码调用b方法,synchronized可重入,再次调用b方法中的lock。此时,isLocked为true,但是,只有lockBy和当前线程相等,不满足wait操作条件,因此,

    它会再次执行后面的代码,于是,lockCount=2。然后,在输出b之后,它会执行b方法中的unlock,解锁,但是,要明确,只有当线程1将它所有的锁都释放完毕后,才会去通知那些wait等待的线程。因此,需要加入对lockCount的判断,只有lockCount为0时,才将isLock的标志位改为false,同时通知其他线程可以去获取锁了。

    参考资料:

    《java并发编程与实战》龙果学院

  • 相关阅读:
    BOOST 信号与槽,获取槽函数返回值,使用占位参数传递信号携带的参数
    单例模式,reorder详解,线程安全,双检查锁
    编程源自生活:抽象 -> 生活中的洗头问题
    前置声明透彻理解,以及和直接头文件包含的区别,注意事项
    三种工厂模式详解
    个人作品1
    Python基本汇总问题
    Json字符串转换成Json对象
    html div可以进行编辑
    父页面访问iframe页面的js
  • 原文地址:https://www.cnblogs.com/pony1223/p/9405005.html
Copyright © 2020-2023  润新知