MST
星途 面试题库

面试题:Java中synchronized和Lock对锁的获取与释放机制对比

详细说明Java中synchronized是如何自动获取和释放锁的,而Lock又是怎样手动获取和释放锁的,以及这种差异在实际应用场景中有何影响。
29.0万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

synchronized 自动获取和释放锁机制

  1. 获取锁: 当一个线程进入被synchronized修饰的同步代码块或方法时,会自动尝试获取对象的锁。如果锁可用,线程会获取锁并进入同步区域执行代码;如果锁不可用,线程会被阻塞,进入等待队列,直到锁被释放。 例如,对于实例方法:
public class SynchronizedExample {
    public synchronized void synchronizedMethod() {
        // 同步代码
    }
}

这里,synchronizedMethod方法被synchronized修饰,线程调用该方法时会尝试获取this对象的锁。 对于静态方法:

public class StaticSynchronizedExample {
    public static synchronized void staticSynchronizedMethod() {
        // 同步代码
    }
}

此时,线程调用staticSynchronizedMethod方法时会尝试获取StaticSynchronizedExample.class类对象的锁。 对于同步代码块:

public class SynchronizedBlockExample {
    private final Object lock = new Object();
    public void synchronizedBlock() {
        synchronized (lock) {
            // 同步代码
        }
    }
}

线程进入同步代码块时会尝试获取lock对象的锁。 2. 释放锁: 当线程执行完同步代码块或方法,或者在同步代码块或方法中抛出异常时,会自动释放锁。这意味着,无论代码以何种方式正常结束或异常结束,锁都会被释放,等待队列中的其他线程有机会获取锁。

Lock 手动获取和释放锁机制

  1. 获取锁: 使用Lock接口实现类(如ReentrantLock)时,线程需要调用lock()方法手动获取锁。如果锁不可用,线程会被阻塞,直到获取到锁。 例如:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();
    public void lockMethod() {
        lock.lock();
        try {
            // 同步代码
        } finally {
            lock.unlock();
        }
    }
}

在上述代码中,线程调用lockMethod方法时,通过lock.lock()尝试获取锁。如果锁不可用,线程会在此处阻塞。 2. 释放锁: 获取锁后,必须手动调用unlock()方法释放锁。为确保锁一定会被释放,通常将unlock()方法放在finally块中,这样即使在同步代码中抛出异常,锁也能被正确释放。

差异在实际应用场景中的影响

  1. 灵活性
    • Lock提供了更灵活的锁获取方式。例如,tryLock()方法可以尝试获取锁,如果锁不可用立即返回false,而不是像synchronized那样一直阻塞。这在某些场景下(如尝试获取锁多次,若都失败则执行其他逻辑)非常有用。
    • Lock还支持公平锁(构造ReentrantLock时传入true),按照线程等待的顺序分配锁,而synchronized是非公平锁,在高并发场景下,可能会导致某些线程长时间等待。
  2. 异常处理
    • synchronized在发生异常时会自动释放锁,不需要额外处理。
    • 使用Lock时,由于手动释放锁,若在同步代码中发生异常而未在finally块中正确释放锁,会导致锁永远无法释放,其他线程一直阻塞。所以使用Lock时需要更严谨的异常处理。
  3. 性能
    • 在低竞争场景下,synchronizedLock性能相近。
    • 在高竞争场景下,Lock的性能优势可能更明显,因为它可以使用非公平锁提高吞吐量,并且在锁的获取和释放上有更细粒度的控制。但这也依赖于具体的应用场景和代码实现。