sychnized和Lock什么区别?sychnize 什么情况情况是对象锁? 什么时候是全局锁为什么?
今天咱们聊聊一个在面试中经常碰到的经典问题——synchronized 和 Lock 有什么区别? 还有,synchronized 是什么时候是对象锁,什么时候是全局锁?看似简单的问题,其实涉及的内容不少,搞不好你就会在面试官面前掉链子。不要慌,接下来我会带你理清这几个概念,顺便给你补充一些有趣的段子,保证让你学得轻松,记得牢。
一、synchronized和Lock的基本区别
首先,我们要明确synchronized和Lock的基本区别。就像在开发过程中,咱们写代码时,有时候需要锁定资源来防止多线程并发带来的问题。Java提供了两种方式来实现同步:synchronized关键字和Lock接口。
1. synchronized
synchronized是Java中最早期的同步机制,它可以用在方法或者代码块上。通过它,Java会确保同一时间内只有一个线程能访问被锁定的资源,从而避免并发冲突。
使用方法:
public synchronized void someMethod() {
// 线程安全的代码块
}
或者:
public void someMethod() {
synchronized (this) {
// 线程安全的代码块
}
}
sychronized的特点是:
自动解锁:当线程退出同步块时,锁会自动释放,无需手动解锁。 简单易用:只要在方法或者代码块前加上 synchronized,就能实现同步。限制:它是阻塞的,这意味着线程会等待,直到获得锁。
代码解释:
当一个线程进入 someMethod时,其他线程如果尝试进入同一个方法,会被阻塞,直到第一个线程执行完毕并释放锁。如果你把 synchronized应用到整个方法上,那么该方法就是一个对象锁,也就是整个对象都会被锁住。
2. Lock接口
Lock是从Java 5开始引入的同步机制,它提供了比synchronized更多的灵活性。通过Lock,我们可以更细粒度地控制锁的获取与释放。
使用方法:
Lock lock = new ReentrantLock();
lock.lock();
try {
// 线程安全的代码块
} finally {
lock.unlock();
}
Lock的特点是:
手动加锁与解锁:你需要显式地调用 lock()方法来加锁,调用unlock()来解锁。非阻塞:通过 tryLock()方法,可以尝试获取锁,而不会阻塞线程。灵活性高:它不仅支持公平锁和非公平锁,还支持可重入锁等特性。
代码解释:
lock.lock()是用来加锁的,只有当线程成功获取锁后才能进入执行区块。lock.unlock()释放锁。最常见的错误是忘记释放锁,这会导致死锁问题(多线程永远等待对方释放锁)。
区别总结:
灵活性: Lock提供了更多的控制,允许你显式地加锁和解锁。自动 vs 手动解锁: synchronized是自动的,Lock需要手动释放锁。性能:在某些高并发的场景下, Lock的性能比synchronized更好,因为它支持尝试锁定,避免了阻塞。
二、synchronized是对象锁还是全局锁?
这个问题其实也挺有意思。我们先来从Java对象锁的角度理解。synchronized在不同场景下其实有两种不同的锁类型:对象锁和类锁(全局锁)。
1. 对象锁
当你在一个实例方法上使用synchronized时,它会给当前对象加锁。也就是说,只有一个线程能访问该对象的该方法。
public class MyClass {
public synchronized void methodA() {
// 只有一个线程能执行这个方法
}
}
在这个例子中,methodA()是对象锁。意思就是,如果有多个线程同时访问MyClass的实例方法,只有一个线程能执行methodA,其他线程会被阻塞,直到第一个线程执行完毕并释放锁。
2. 类锁(全局锁)
当你在静态方法上使用synchronized时,它会给整个类加锁。也就是说,整个类的所有实例都会共享同一个锁。
public class MyClass {
public synchronized static void methodA() {
// 只有一个线程能执行这个方法
}
}
在这个例子中,methodA()是全局锁,因为它是一个静态方法。此时,即使有多个MyClass的实例,只有一个线程可以执行该静态方法methodA。其他线程尝试执行该方法时,会被阻塞。
三、什么时候使用对象锁,什么时候使用类锁(全局锁)?
1. 对象锁:适用于实例级别的同步
当你希望限制某个对象的实例方法只能被一个线程访问时,你应该使用对象锁。这种情况通常发生在每个对象的状态需要同步更新时,比如缓存、文件操作等。
例如,一个类的实例用来模拟一个文件读写器,每个对象只能被一个线程读取或写入。此时就应该用对象锁来同步。
2. 类锁(全局锁):适用于类级别的同步
当你希望限制类的所有实例共享的资源时,就应该使用类锁(全局锁)。这种情况通常发生在操作静态变量、静态方法等类级资源时。因为静态变量和方法是所有对象共享的,所以需要确保对它们的访问是同步的。
比如,有一个全局配置类,它的配置信息在多个线程间共享,此时就需要使用类锁来保证配置加载时的线程安全。
四、总结:synchronized vs Lock vs 对象锁 vs 类锁
synchronized:简单易用,但不如 Lock灵活;适合快速实现同步。Lock:灵活性更高,适用于更复杂的并发场景,尤其是高并发环境下。 对象锁:适用于实例方法的同步,每个对象独立锁。 类锁(全局锁):适用于静态方法和静态变量的同步,类级别的资源共享。
五、面试总结
在面试中,面试官不仅希望你能理解这些技术点,还希望你能结合实际应用场景来选择合适的同步方式。千万不要在面试时,看到synchronized和Lock这些问题就开始“掉进坑里”,只会谈一些书面知识。面试官更希望看到你如何灵活运用它们,解决实际问题。你得知道每个同步机制的利弊,以及在什么场景下最合适。
至于对象锁和全局锁的选择,也并不是一成不变的。比如,某些情况下使用对象锁能提高性能,而有些情况下使用类锁能避免多个线程间的竞争。理解这些场景,才能在面试时给面试官留下深刻的印象。
-END-
以上,就是今天的分享了,看完文章记得右下角给何老师点赞,也欢迎在评论区写下你的留言。