文章目录
写在文章开头
在多线程编程中,确保数据的一致性和线程安全是至关重要的问题。Java 语言提供了多种机制来实现这一目标,其中 synchronized 关键字是最常用且最基础的一种同步工具。本文将通过具体的使用示例和详细的底层原理分析,帮助读者全面理解 synchronized 的工作方式及其在实际开发中的应用。

Hi,我是 sharkChili ,是个不断在硬核技术上作死的技术人,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili 。
同时也非常欢迎你star我的开源项目mini-redis:https://github.com/shark-ctrl/mini-redis
因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。
synchronized是什么?有什么用?
synchronized是在多线程场景经常用到的关键字,通过synchronized将共享资源设置为临界资源,确保并发场景下共享资源操作的正确性:

synchronized基础使用示例
作用于静态方法
synchronized作用于静态方法上,锁的对象为Class,这就意味着方法的调用者无论是Class还是实例对象都可以保持互斥,所以下面这段代码的结果为200:
public class SynchronizedDemo {
private static Logger logger = LoggerFactory.getLogger(SynchronizedDemo.class);
private static int count = 0;
/**
* synchronized作用域静态类上
*/
public synchronized static void method() {
count++;
}
@Test
public void test() {
IntStream.rangeClosed(1,1_0000)
.parallel()
.forEach(i->SynchronizedDemo.method());
IntStream.rangeClosed(1,1_0000)
.parallel()
.forEach(i->new SynchronizedDemo().method());
logger.info("count:{}",count);
}
}
输出结果
22:59:44.647 [main] INFO com.sharkChili.webTemplate.SynchronizedDemo - count:20000
作用于对象方法
作用于方法上,则锁住的对象是调用的示例对象,如果我们使用下面这段写法,最终的结果却不是10000。
private static Logger logger = LoggerFactory.getLogger(SynchronizedDemo.class);
private static int count = 0;
/**
* synchronized作用域实例方法上
*/
public synchronized void method() {
count++;
}
@Test
public void test() {
IntStream.rangeClosed(1,1_0000)
.parallel()
.forEach(i->new SynchronizedDemo().method());
logger.info("count:{}",count);
}
}
输出结果
2023-03-16 21:03:44,300 INFO SynchronizedDemo:30 - count:8786
因为synchronized 作用于实例方法,会导致每个线程获得的锁都是各自使用的实例对象,而++操作又非原子操作,导致互斥失败进而导致数据错误。
什么是原子操作呢?通俗的来说就是一件事情只要一条指令就能完成,而count++在底层汇编指令如下所示,可以看到++操作实际上是需要3个步骤完成的:
- 从内存将
count读取到寄存器 count自增- 写回内存
__asm
{
moveax, dword ptr[i]
inc eax
mov dwordptr[i], eax
}
正是由于锁互斥的失败,导致两个线程同时到临界区域加载资源,获得的count都是0,经过自增后都是1,导致数据少了1。

所以正确的使用方式是多个线程使用同一个对象调用该方法
SynchronizedDemo demo = new SynchronizedDemo();
IntStream.rangeClosed(1,1_0000)
.parallel()
.forEach(i->demo.method());
logger.info("count:{}",count);
这样一来输出的结果就正常了。
2023-03-16 23:08:23,656 INFO SynchronizedDemo:31 - count:10000
作用于代码块
作用于代码块上的synchronized锁住的就是括号内的对象实例,以下面这段代码为例,锁的就是当前调用者:
//锁住当前调用实例
public void method() {
synchronized (this) {
count++;
}
}
所以我们的使用的方式还是和作用与实例方法上一样:
SynchronizedDemo demo = new SynchronizedDemo();
IntStream.rangeClosed(1, 1_0000)
.parallel()
.forEach(i -> demo.

本文详细介绍了Java中的synchronized关键字,包括其作用、使用方式、实现原理以及锁的升级过程。synchronized用于保证多线程环境下的线程安全,通过对象头的MarkWord实现锁的管理。锁升级从无锁到偏向锁、轻量级锁再到重量级锁,以适应不同并发场景,降低锁开销。此外,文章还讨论了synchronized与ReentrantLock的区别,以及在使用中需要注意的事项,如锁的正确使用、避免死锁等问题。通过对锁的深入理解,有助于提升Java并发编程的效率和安全性。
最低0.47元/天 解锁文章
1927

被折叠的 条评论
为什么被折叠?



