synchronized 和 ReentrantLock (Lock)区别,优劣对比

两种方法都是为了确保多线程环境中的线程安全,但它们使用了不同的同步机制:synchronized 关键字和 Lock 接口。下面详细对比这两种方法的区别、优缺点以及适用场景。

synchronized 关键字

public synchronized void addSession(HttpSession session) {
    if (session != null) {
        sessionMap.put(session.getId(), session);
    }
}

优点

  1. 简单易用:synchronized 关键字内置于 Java 语言中,使用方便,容易理解。
  2. 隐式锁:锁的获取和释放由 JVM 自动管理,不需要显式的解锁操作,减少了编程错误的可能性。
  3. 异常处理:锁的释放是自动的,即使抛出异常也会自动释放锁,避免了死锁的风险。

缺点

  1. 灵活性低:无法中断正在等待的线程,除非超时(使用 wait 和 notify 机制)。
  2. 性能问题:在高并发情况下,synchronized 的性能可能不如 Lock 接口,因为 synchronized 只能是阻塞的,无法实现更高级的并发控制。
  3. 不可尝试获取锁:不能尝试获取锁,并在获取不到时执行其他逻辑。

Lock 接口

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

    public void addPlayer(Integer userId, Integer rating, Integer selected_bot_id) {
        lock.lock();
        try {
            players.add(new Player(userId, rating, selected_bot_id, 0));
        } finally {
            lock.unlock();
        }
    }
}

优点

  1. 灵活性高:提供了多种锁获取模式(可中断、不可中断、限时等待等),适用于更复杂的并发控制。
  2. 性能好:在高并发环境中,Lock 接口的性能可能优于 synchronized,特别是在需要频繁锁定和解锁的情况下。
  3. 可尝试获取锁:可以尝试获取锁,并在获取不到时执行其他逻辑,避免长时间等待。

缺点

  1. 复杂性高:需要显式地获取和释放锁,容易出现忘记解锁的编程错误,从而导致死锁。
  2. 异常处理复杂:需要在 finally 块中显式释放锁,增加了代码的复杂性。

对比总结

image

使用场景

synchronized
适用于简单的同步需求,代码块较短,锁竞争不激烈的情况。
适合初学者,简单易用,降低编程错误。
Lock 接口:
适用于复杂的并发控制需求,代码块较长,锁竞争激烈的情况。
需要更高的性能和灵活性,能处理更多并发场景(如可中断锁、限时锁等)。

结论

选择 synchronized:如果你的同步需求较简单,代码块较短且锁竞争不激烈,使用 synchronized 更加方便且安全。

选择 Lock 接口:如果你的同步需求较复杂,代码块较长且锁竞争激烈,使用 Lock 接口可以提供更高的性能和灵活性。

posted @ 2024-06-21 14:00  r涤生  阅读(77)  评论(0编辑  收藏  举报