持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情
Java中synchronized的锁优化
在并发编程中,对于锁的使用是相当频繁的,所以相信各位朋友们对如何用锁的认识都有一定深度了,那么既然这样,今天我们就来学习另外一个东西——锁是如何优化的,可以有什么优化?
锁优化
下面我们先来看一个常见的例子:
有一个方法,它是需要在并发环境下也保持着同步执行的,所以一般情况下,我们会像下面这样给方法加锁:
具体代码如下:
synchronized void testMethod() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
count ++;
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以看到上述代码在方法头加了synchronized,这就可以保证了整个方法都是同步执行的。
但是在实际上,我们可能并不是整个方法都要求同步的,也就是说方法内部有一些代码逻辑是可以并发执行的;而如果是直接在方法头加synchronized的话,就整个方法都是同步的了,那些允许并发的代码也必须同步,这样很显然会影响整个代码的执行效率。
为此,我们提出了锁优化的概念,当然啦,在这种情况下,其实就是将锁的粒度降低(整个方法都上锁导致锁的粒度太大了)
下面我们一起来看看降低锁粒度之后的代码实现:
void testMethod() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 这里可能存在部分不需要同步的代码
// 下面是必须保持同步的代码逻辑
synchronized(o) {
count ++;
}
// 这里可能存在部分不需要同步的代码
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以看到上述代码中synchronized锁住的就只要同步逻辑 count ++;了,锁粒度降低,其他代码可以并发执行,整体代码执行效率上升。
锁优化思想在单例模式中的体现
我们都知道在单例模式中,有一种双重检查的实现方法,那么你了解的其中蕴含的锁优化的思想么?
下面我们一起来看看双重检查的核心代码:
class Instance {
volatile Instance instance;
void getInstance() {
...
// 第一重检查
if (instance != null) {
synchronized (o) {
// 第二重的检查
if (instance != null) {
instance = new Instance();
}
}
}
...
return instance;
}
}
在上述代码中,其实synchronized加第二重检查已经可以确保生成的是单例了的,为什么还需要第一重检查呢?第一重检查的作用是什么?
其实我们都知道加锁之后,性能肯定会下降的!
有了第一重检查在,可以拦截掉大部分的请求(在单例模式中,只需要创建一次,其他不为空就可以直接跳过同步创建Instance的代码逻辑了),这样避免多次执行同步代码而引发的性能下降。