synchronized与ReentrantLock

  • synchronizedReentrantLock的对比主要在于以下四个方面:
    1. 可重入性:synchronized是可重入锁,ReentrantLock也是可重入锁。
    2. 依赖:synchronized依赖于JVM,而ReentrantLock依赖于API。
    3. 功能:ReentrantLock相比synchronized提供了更多的功能,主要有四点:
      1. 等待可中断
      2. 可实现公平锁
      3. 可实现选择性通知
      4. 支持超时
    4. 是否可中断:ReentrantLock属于可中断锁,而synchronized属于不可中断锁。

1. 可重入性

  • 可重入锁也叫递归锁,指的是线程可以再次获取自己的内部锁。
  • JDK提供的所有现成的Lock实现类,以及synchronized关键字都是可重入锁。
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class SynchronizedDemo {
    public synchronized void methodA() {
    System.out.println("In method A");
    methodB(); // 再次获取锁
    }

    public synchronized void methodB() {
    System.out.println("In method B");
    }
    }
  • 在上述示例中,同一个线程在调用methodA时获取了当前对象的锁,执行methodB时可以再次获取当前对象的锁,不会产生死锁问题。

2. 依赖

  • synchronized是Java语言内置的同步机制,依赖于JVM的实现。
  • ReentrantLock是JDK层面实现的,也就是API层面,需要使用lock()unlock()方法配合try/finally来使用。

3. 功能

  • ReentrantLock相比synchronized提供了更多的功能,主要有以下四点:
    • 等待可中断:ReentrantLock中的lock.lockInterruptibly()方法允许线程在等待锁的过程中被中断。也就是当前线程在等待获取锁的过程中被其他线程使用interrupt()中断,当前线程就会抛出InterruptedException异常,可以捕捉该异常并进行处理。
    • 可实现公平锁:ReentrantLock可以选择使用公平锁还是非公平锁,而synchronized只能是非公平锁。公平锁是指线程获取锁的顺序是按照请求锁的顺序来分配的,而非公平锁则不保证顺序。ReentrantLock默认使用非公平锁。
      1
      2
      3
      4
      // 传入一个boolean参数,true表示公平锁,false表示非公平锁
      public ReentrantLock(boolean fair) {
      sync = fair ? new FairSync() : new NonfairSync();
      }
    • 可实现选择性通知:synchronized关键字与wait()notify()/notifyAll()方法相结合可以实现等待/通知机制。但是synchronized只能使用一个等待队列,而ReentrantLock可以通过Condition接口和newCondition)_方法创建多个等待队列,从而实现更灵活的等待/通知机制。
    • 支持超时:ReentrantLock提供的tryLock(timeout)方法可以指定等待获取锁的最长等待时间,超过这个时间就会获取锁失败,不会一直等待。

4. 是否可中断

  • ReentrantLock属于可中断锁,而synchronized属于不可中断锁。