我们昨天说过了关于这个 Java 的 volatile 关键字了,但是我们还需要知道一个关键字,那么就是 synchronized 这个关键字,为什么呢?因为在开发的过程中我们会经常的使用到这个关键字,但是呢,又会有很多的人对这个理解的不明白,并且,和 lock 一起给混淆掉,今天了不起就来说说这个 synchronized 和 lock 的区别。
synchronized
synchronized 是Java中的一个关键字,用于控制对共享资源的并发访问,从而防止多个线程同时访问某个特定资源,这被称为同步。这个关键字可以用来修饰方法或代码块。
修饰方法
当synchronized修饰一个方法时,它表示整个方法体都是同步的,即同时只能有一个线程可以执行这个方法。
代码示例:
public synchronized void synchronizedMethod() {
// 方法体
}
修饰代码块
synchronized也可以用来修饰一个代码块,这时需要指定一个锁对象。当一个线程进入synchronized代码块时,它需要获得这个锁对象的监视器锁,如果锁已经被其他线程持有,则该线程将被阻塞,直到锁被释放。
public void method() {
synchronized (this) {
// 代码块
}
}
在这个例子中,this是锁对象。你也可以使用其他对象作为锁。
我们需要注意的几点内容:
- synchronized锁是可重入的,也就是说,一个线程可以多次获得同一个锁而不会发生死锁。
- 使用synchronized需要谨慎,因为不当的使用可能导致死锁或性能问题。
- synchronized是一种内置锁,也被称为互斥锁或监视器锁。Java中的每个对象都有一个与之关联的监视器锁。
- synchronized关键字的实现是基于JVM的,因此它的行为可能因JVM的实现而异。
我们总结一下:
synchronized 可以给类,方法,代码块加锁。
那么 Lock 呢?
LOCK
Java 的 Lock 接口及其实现类提供了一种比 synchronized 关键字更加灵活和可控制的锁机制。Lock 接口在 java.util.concurrent.locks 包中定义,它允许更细粒度的控制,包括尝试获取锁、定时获取锁以及可中断地获取锁等能力。
Lock 接口的主要方法:
而这个 Lock 的主要实现类就是ReentrantLock。
也就是可重入锁,意味着一个线程可以多次获取同一个锁而不会发生死锁。它提供了与 synchronized 类似的功能,但提供了更多的灵活性。
我们看一段代码示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
return count;
}
}
在这个例子中,Counter 类使用了一个 ReentrantLock 来确保 increment 方法的原子性。每次调用 increment 方法时,都会先获取锁,然后增加计数器,最后释放锁。
LOCK 和 synchronized 的比较
灵活性: Lock 提供了更灵活的锁获取方式,包括尝试获取和定时获取,而 synchronized 不支持这些功能。
等待可中断: Lock 的获取操作可以被中断,而 synchronized 的等待不能被中断。
锁分离: Lock 允许将等待/通知机制与锁分离,通过 Condition 对象来实现,而 synchronized 的等待/通知是与对象锁关联的。
性能: 在某些情况下,ReentrantLock 可能比 synchronized 提供更好的性能,特别是在高竞争的场景下,但这也取决于具体的使用情况。
语法简洁性: synchronized 的语法更简洁,适合简单的同步需求。
所以大家在选择使用 Lock 还是 synchronized 取决于具体的应用场景和需求。在需要更高级功能或更高性能的场景下,Lock 可能是更好的选择。在简单的同步需求下,synchronized 通常更易于使用和理解。
但是他们的底层区别在哪呢?
lock 和 synchronized 底层原理区别
Synchronized是Java语言内置的关键字,它的实现是基于JVM的,源码在JVM中,用C++语言实现。其锁机制是基于对象头的Mark Word来实现的,包括偏向锁、轻量级锁和重量级锁。当线程尝试进入synchronized代码块或方法时,JVM会根据当前对象的锁状态以及线程的锁请求来进行相应的处理。
Lock是一个接口,它的实现类如ReentrantLock是由JDK提供的,用Java语言实现。Lock的实现是基于Java代码的,它通过内部的AbstractQueuedSynchronizer(AQS)框架来实现锁的获取、释放以及线程等待和唤醒等功能。AQS框架是JDK中提供的一个用于构建锁和同步器的框架,它维护了一个FIFO的队列来管理等待获取锁的线程。
对于他们的区别,你理解了多少呢?