欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

可重入锁和不可重入锁

程序员文章站 2024-01-08 08:21:34
...

锁就是把代码块、资源或数据(称为临界资源)锁上,访问临界资源的时候只允许一个线程去操作,其他线程必须等待或者放弃,这是为了保证最终程序的正确运行。

不可重入锁

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锁:

public class Test{
    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        test.print();
    }
    Lock lock = new Lock();

    public void print() throws InterruptedException {
        lock.lock();
        add();
        lock.unlock();
    }

    public void add() throws InterruptedException {
        lock.lock();
        // do something
        lock.unlock();
    }
}

这段程序运行会出现死循环。当调用print()方法的时候,线程获取到锁,然后调用add()方法,线程再次尝试获取锁,被阻塞。add()方法要等print()方法执行结束才能获取锁,而print()方法要等add()方法执行结束才能释放锁,导致死锁。
当调用print()方法时,获得了锁,再未释放锁之前,这个线程就不能再调用其他方法,在其他方法中尝试获取锁。这种锁称为不可重入锁,也叫自旋锁

可重入锁

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;
        lockedBy = thread;
        lockedCount++;
    }

    public synchronized void unlock() {
        lockedCount--;

        // 该线程中,获取锁的程序都执行了释放锁操作,线程才真正释放锁
        if(0 == lockedCount) {
            isLocked = false;
            notify();
        }
    }
}

当第一个线程执行print()方法的时候,获取到了锁,lockedBy等于该线程,该线程调用add()方法时,尝试获取锁,isLocked值为true,即锁已经被占用,但是lockedBy等于该线程,所以add()方法可以获取锁,lockedCount的值此时为2。当第二个线程执行print方法的时候,isLocked值为true,并且lockedBy不等于当前线程成立,因此进入等待状态。这就是可重入锁。

可重入锁比不可重入锁多了两个属性:lockedBy表示锁被哪个线程获取,lockedCount表示一个线程内,锁被获取到的次数。根据lockedBy属性,使当前线程的不同代码块可以获取多次锁,根据lockedCount属性,执行真正释放锁的逻辑。

Java中常用的可重入锁

  • synchronized
  • java.util.concurrent.locks.ReentrantLock

上一篇:

下一篇: