zoukankan      html  css  js  c++  java
  • 可重入锁

    锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) 。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及。

    四、可重入锁:

    本文里面讲的是广义上的可重入锁,而不是单指JAVA下的ReentrantLock。

    可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
    在JAVA环境下ReentrantLock 和synchronized 都是可重入锁。

    下面是synchronized的可重入锁的实例:

    package com.dxz.sync3;
    
    public class Test implements Runnable {
    
        public synchronized void get() {
            System.out.println(Thread.currentThread().getId());
            set();
        }
    
        public synchronized void set() {
            System.out.println(Thread.currentThread().getId());
        }
    
        @Override
        public void run() {
            get();
        }
    
        public static void main(String[] args) {
            Test ss = new Test();
            new Thread(ss).start();
            new Thread(ss).start();
            new Thread(ss).start();
        }
    }

    下面是ReentrantLock的可重入锁的实例:

    package com.dxz.sync4;
    
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test implements Runnable {
        ReentrantLock lock = new ReentrantLock();
    
        public void get() {
            lock.lock();
            System.out.println(Thread.currentThread().getId());
            set();
            lock.unlock();
        }
    
        public void set() {
            lock.lock();
            System.out.println(Thread.currentThread().getId());
            lock.unlock();
        }
    
        @Override
        public void run() {
            get();
        }
    
        public static void main(String[] args) {
            Test ss = new Test();
            new Thread(ss).start();
            new Thread(ss).start();
            new Thread(ss).start();
        }
    }

    两个例子最后的结果都是正确的,即 同一个线程id被连续输出两次。

    结果如下:

    Threadid: 8
    Threadid: 8
    Threadid: 10
    Threadid: 10
    Threadid: 9
    Threadid: 9

    可重入锁最大的作用是避免死锁。


    我们以自旋锁作为例子,

    package com.dxz.sync4;
    
    import java.util.concurrent.atomic.AtomicReference;
    
    public class SpinLock {
        private AtomicReference<Thread> owner = new AtomicReference<>();
    
        public void lock() {
            Thread current = Thread.currentThread();
            while (!owner.compareAndSet(null, current)) {
            }
        }
    
        public void unlock() {
            Thread current = Thread.currentThread();
            owner.compareAndSet(current, null);
        }
    }

    对于自旋锁来说,
    1、若有同一线程两调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁。说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
    2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。
    (采用计数次进行统计)
    修改之后,如下:

    package com.dxz.sync4;
    
    import java.util.concurrent.atomic.AtomicReference;
    
    public class SpinLock1 {
        private AtomicReference<Thread> owner = new AtomicReference<>();
        private int count = 0;
    
        public void lock() {
            Thread current = Thread.currentThread();
            if (current == owner.get()) {
                count++;
                return;
            }
    
            while (!owner.compareAndSet(null, current)) {
    
            }
        }
    
        public void unlock() {
            Thread current = Thread.currentThread();
            if (current == owner.get()) {
                if (count != 0) {
                    count--;
                } else {
                    owner.compareAndSet(current, null);
                }
    
            }
    
        }
    }

    该自旋锁即为可重入锁。

    可重入锁也支持在父子类继承的环境中,示例:

    package com.dxz.sync;
    
    public class Main {
        public int i = 10;
    
        public synchronized void operateMainMethod() {
            try {
                i--;
                System.out.println("main print i = " + i);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }
        }
    }

    子类:

    package com.dxz.sync;
    
    public class Sub extends Main {
        public synchronized void operateISubMethod() {
            try {
                while (i > 0) {
                    i--;
                    System.out.println("sub print i=" + i);
                    Thread.sleep(100);
                    this.operateMainMethod();
                }
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }
        }
    }

    线程:

    package com.dxz.sync;
    
    public class MyThread extends Thread {
        @Override
        public void run() {
    
            Sub sub = new Sub();
            sub.operateISubMethod();
        }
    }

    入口类:

    package com.dxz.sync;
    
    public class Test {
    
        public static void main(String[] args) {
    
            MyThread thread = new MyThread();
            thread.start();
        }
    
    }

    结果:

    sub print i=9
    main print i = 8
    sub print i=7
    main print i = 6
    sub print i=5
    main print i = 4
    sub print i=3
    main print i = 2
    sub print i=1
    main print i = 0

    可重入锁也支持在父子类继承的环境中。

    下面这个锁的实现是不可重入的:

    package com.dxz.sync3;
    
    public class Lock {
        private boolean isLocked = false;
    
        public synchronized void lock() throws InterruptedException {
            while (isLocked) {
                wait();
            }
            isLocked = true;
        }
    
        public synchronized void unlock() {
            isLocked = false;
            notify();
        }
    }

    如果一个线程在两次调用lock()间没有调用unlock()方法,那么第二次调用lock()就会被阻塞,这就出现了重入锁死。

    避免重入锁死有两个选择:

    1. 编写代码时避免再次获取已经持有的锁
    2. 使用可重入锁

    至于哪个选择最适合你的项目,得视具体情况而定。可重入锁通常没有不可重入锁那么好的表现,而且实现起来复杂,但这些情况在你的项目中也许算不上什么问题。无论你的项目用锁来实现方便还是不用锁方便,可重入特性都需要根据具体问题具体分析。

  • 相关阅读:
    Leetcode Substring with Concatenation of All Words
    Leetcode Divide Two Integers
    Leetcode Edit Distance
    Leetcode Longest Palindromic Substring
    Leetcode Longest Substring Without Repeating Characters
    Leetcode 4Sum
    Leetcode 3Sum Closest
    Leetcode 3Sum
    Leetcode Candy
    Leetcode jump Game II
  • 原文地址:https://www.cnblogs.com/duanxz/p/6705886.html
Copyright © 2011-2022 走看看