可重入锁和不可重入锁

不可重入锁

先来设计一种锁

public class Lock{
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException{
        while(isLocked){    
            wait();//把当前线程wait
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();
    }
}

这其实是个不可重入锁,举个例子

public class Count{
    Lock lock = new Lock();
    public void print(){
        lock.lock();
        doAdd();
        lock.unlock();
    }
    public void doAdd(){
        lock.lock();
        //do something
        lock.unlock();
    }
}

  当调用print()方法时,获得了锁,这时就无法再调用doAdd()方法,这时必须先释放锁才能调用,所以称这种锁为不可重入锁,也叫自旋锁。

可重入锁

public class Lock{
    boolean isLocked = false;
    Thread  lockedBy = null;
    int lockedCount = 0;
    public synchronized void lock()
            throws InterruptedException{
        Thread thread = Thread.currentThread();
        while(isLocked && lockedBy != thread){
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = thread;
    }
    public synchronized void unlock(){
        if(Thread.currentThread() == this.lockedBy){
            lockedCount--;
            if(lockedCount == 0){//获得该锁的那个线程,获得了多少次该锁(即调用了几次lock方法,即重入了几次),就得unlock几次,即lockedCount=0,才会把那些wait(阻塞)的线程唤醒
                isLocked = false;
                notify();
            }
        }
    }
}

相对来说,可重入就意味着:一个线程可以进入任何一个 该线程 已经拥有的锁所同步着的代码块

  第一个线程执行print()方法,得到了锁,使lockedBy等于当前线程,也就是说,执行的这个方法的线程获得了这个锁,执行add()方法时,同样要先获得锁(即调用lock.lock()),因不满足while循环的条件isLocked(=true,因为该线程调用print()方法时就获得该锁了);但是这里与不可重入锁的区别是有个lockedBy(即表示现在哪个线程持有锁);因为调用add()方法的是和调用print()的是同一个线程,也就是不等待,继续进行,将此时的lockedCount变量,也就是当前获得锁的数量加一,当释放了所有的锁(即得调用获得锁数量次数的unlock),才执行notify()。

  如果在执行这个方法时,有第二个线程想要执行这个方法,因为lockedBy不等于第二个线程,导致这个线程进入了循环,也就是等待(阻塞),不断执行wait()方法。只有当第一个线程释放了所有的锁(即第一个线程调用了多少次lock()方法就得调用多少次unlock()方法释放锁),执行了notify()方法,第二个线程才得以跳出循环,继续执行。

这就是可重入锁的特点。

  可重入锁与不可重入锁对比,简单来说就是:可重入锁会多两个属性(1、获得该锁的线程;2、获得该锁的次数),根据第一个属性判断,如果是持有该锁的那个线程又来lock,不会被阻塞(wait),而是在上锁的次数加一(表示这个线程又锁了一次(重入)),只有该线程unlock的次数达到上锁的次数(即第二个属性等于0),才会唤醒其他线程。

 

java中常用的可重入锁:

synchronized

java.util.concurrent.locks.ReentrantLock

 

https://www.cnblogs.com/dj3839/p/6580765.html

posted @ 2018-07-28 20:55  xdyixia  阅读(9253)  评论(0编辑  收藏  举报