Lock与synchronized 的区别
Lock
接口和 synchronized
关键字都是 Java 中用于实现线程同步的机制,但它们在功能、灵活性和使用方式上存在一些显著的区别。下面是详细的对比和解释:
synchronized
关键字
定义
synchronized
是 Java 中的一个关键字,用于实现线程同步,确保在同一时间只有一个线程可以访问被同步的代码块或方法。
主要特点
- 内置机制:
synchronized
是 Java 语言级别的同步机制,内置在 Java 虚拟机(JVM)中。 - 自动释放锁:当线程退出同步代码块或方法时,锁会自动释放。
- 不可中断:
synchronized
不能被中断,线程必须等待锁的释放。 - 粒度较粗:只能用于同步方法或代码块,粒度较粗。
- 非公平锁:默认是非公平锁,即线程获取锁的顺序不确定。
- 隐式锁:使用
synchronized
时,锁是隐式的,不需要显式地创建和管理锁对象。
示例代码
public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } public static void main(String[] args) throws InterruptedException { SynchronizedExample example = new SynchronizedExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + example.getCount()); } }
Lock
接口
定义
Lock
是 java.util.concurrent.locks
包中的一个接口,提供了比 synchronized
更灵活的锁机制。
主要特点
- 显式锁:
Lock
是显式的,需要显式地创建和管理锁对象。 - 可中断:
Lock
支持可中断锁,线程在等待锁时可以被中断。 - 公平锁和非公平锁:可以创建公平锁和非公平锁。
- 粒度更细:可以更灵活地控制锁的获取和释放。
- 条件变量:提供了
Condition
接口,支持更复杂的线程同步操作。 - 手动释放锁:必须显式地调用
unlock()
方法释放锁,否则可能导致死锁。
示例代码
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class LockExample { private int count = 0; private final Lock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount() { lock.lock(); try { return count; } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { LockExample example = new LockExample(); Thread t1 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 1000; i++) { example.increment(); } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("Final count: " + example.getCount()); } }
synchronized
与 Lock
的主要区别
定义 | Java 关键字,内置在 JVM 中 |
接口 |
锁的获取 | 自动获取和释放锁 | 手动获取和释放锁 |
中断 | 不能被中断,线程必须等待锁的释放 | 可以被中断,线程在等待锁时可以被中断 |
公平性 | 默认是非公平锁 | 可以创建公平锁和非公平锁 |
粒度 | 粒度较粗,只能用于同步方法或代码块 | 粒度更细,可以更灵活地控制锁的获取和释放 |
条件变量 | 无条件变量,只能通过
和
方法实现 | 提供
接口,支持更复杂的线程同步操作 |
隐式 vs 显式 | 隐式锁,不需要显式地创建和管理锁对象 | 显式锁,需要显式地创建和管理锁对象 |
性能 | 在低竞争环境下性能较好,高竞争环境下性能较差 | 在高竞争环境下性能较好 |
详细对比
- 锁的获取和释放synchronized:自动获取和释放锁,当线程退出同步代码块或方法时,锁会自动释放。Lock:必须显式地调用 lock() 方法获取锁,调用 unlock() 方法释放锁。如果不显式释放锁,可能导致死锁。
- 中断synchronized:不能被中断,线程必须等待锁的释放。Lock:支持可中断锁,线程在等待锁时可以被中断,抛出 InterruptedException。
- 公平性synchronized:默认是非公平锁,即线程获取锁的顺序不确定。Lock:可以通过构造函数参数指定锁的公平性,例如 new ReentrantLock(true) 创建公平锁。
- 粒度synchronized:粒度较粗,只能用于同步方法或代码块。Lock:粒度更细,可以更灵活地控制锁的获取和释放,支持更复杂的同步逻辑。
- 条件变量synchronized:通过 Object 类的 wait() 和 notify() 方法实现条件变量。Lock:提供了 Condition 接口,支持更复杂的线程同步操作,可以创建多个条件变量。
- 性能synchronized:在低竞争环境下性能较好,但在高竞争环境下性能较差,因为锁的获取和释放开销较大。Lock:在高竞争环境下性能较好,提供了更多的优化选项,如公平锁和非公平锁的选择。
使用场景
- synchronized 关键字:适用于简单的同步场景,代码简洁。适用于不需要复杂同步逻辑的场景。适用于低竞争环境下的同步需求。
- Lock 接口:适用于需要更灵活和复杂的同步逻辑的场景。适用于需要中断支持的场景。适用于需要公平锁的场景。适用于需要手动控制锁的获取和释放的场景。
总结
synchronized
关键字:内置、自动管理、简单易用,适用于简单的同步需求。Lock
接口:显式管理、更灵活、支持更多高级功能,适用于复杂的同步需求。
选择使用 synchronized
还是 Lock
取决于具体的应用需求和并发控制策略。对于简单的同步需求,synchronized
是一个很好的选择;对于复杂的同步需求,Lock
提供了更多的灵活性和功能。