您的位置:

可重入锁和不可重入锁的区别

一、可重入锁与不可重入锁的区别

可重入锁和不可重入锁的最大区别在于,可重入锁允许同一个线程在获得锁之后再次获得该锁,而不可重入锁不允许。

在使用不可重入锁时,如果一个线程已经获得该锁,那么在该线程释放该锁之前,其他所有线程都无法获得这个锁。而在使用可重入锁时,如果一个线程已经获得锁,那么在该线程释放该锁之前,它可以再次获得该锁而不会被阻塞。

二、可重入锁的使用场景

可重入锁通常应用于递归或者嵌套的代码中。在代码嵌套调用的过程中,如果使用不可重入锁,那么程序将会阻塞,可能会导致死锁现象的发生。

举例来说,在使用不可重入锁时,如果在一个线程已经获得锁的情况下,又尝试获得该锁,那么这个线程将会被一直阻塞,直到另外一个线程释放该锁。而在使用可重入锁时,同一个线程可以多次获得锁,因此不会导致死锁现象的发生。

三、可重入锁的实现原理

可重入锁最常用的实现方式是通过计数器来实现的,每次获得锁时,计数器加1,每次释放锁时,计数器减1。只有当计数器为0时,其他线程才有机会获得该锁。这样就能够保证同一个线程可以多次获得该锁,而其他线程只有在该锁被彻底释放时才能获得该锁。

四、synchronized可重入锁

在Java语言中,synchronized关键字是一种可重入锁。这意味着如果一个线程已经获得了某个对象上的synchronized锁,那么它可以再次获得该对象锁而不会被阻塞。

synchronized锁的实现方式并不是通过计数器来实现的。它是通过线程持有的对象监视器(也可以称为锁对象)来实现的。当线程请求进入一段同步代码时,如果该锁没有被其他线程占用,那么该线程会获得该锁,同时锁对象的计数器会+1。如果同一线程再次请求该锁,那么线程可以再次获得该锁,同时计数器也会再次+1。当线程执行完同步代码块并释放锁时,该线程持有的锁对象的计数器会-1。只有当该计数器的值为0时,锁对象才会被解锁,这样其他线程才有机会获得该锁。

五、可重入锁和不可重入锁的区别

1、可重入锁允许同一个线程在获得锁之后再次获得该锁,而不可重入锁不允许。

2、在使用不可重入锁时,如果一个线程已经获得该锁,那么在该线程释放该锁之前,其他所有线程都无法获得这个锁。而在使用可重入锁时,如果一个线程已经获得锁,那么在该线程释放该锁之前,它可以再次获得该锁而不会被阻塞。

3、可重入锁通常用于递归嵌套的代码中,而不可重入锁一般只用于独占式地获得资源。

示例代码

// 可重入锁的示例
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    work();
                }
            }).start();
        }
    }

    private static void work() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "获得了锁");
            nestedWork();
        } finally {
            lock.unlock();
        }
    }

    private static void nestedWork() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "再次获得了锁");
        } finally {
            lock.unlock();
        }
    }
}

// 不可重入锁的示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class NonReentrantLockDemo {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                work();
            }
        }, "Thread-A").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                work();
            }
        }, "Thread-B").start();
    }

    private static void work() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "获得了锁");
            nestedWork();
        } finally {
            lock.unlock();
        }
    }

    private static void nestedWork() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "尝试获得锁但被阻塞了");
        } finally {
            lock.unlock();
        }
    }
}