1. 原子性
原子性指的是一个操作或一系列操作要么全部执行完成并且不被打断,要么完全不执行。synchronized
保证了被同步块包围的代码在任意时刻只能被一个线程执行。
原理分析:
- 互斥锁 (Mutex Lock):
synchronized
通过实现互斥锁来实现原子性。每个对象在 Java 中都有一个隐式锁(即 monitor 锁),当一个线程获取该对象的锁后,其他线程不能获取同一个锁,因此其他线程无法进入同步代码块。 - Monitor 机制:Java 的每一个对象都可以作为一把锁(即 monitor),
synchronized
会在编译时生成monitorenter
和monitorexit
字节码指令:monitorenter
:线程试图获取对象的锁,如果锁被其他线程持有,当前线程将进入阻塞状态,直到锁被释放。monitorexit
:当前线程释放锁,其他等待的线程有机会获取锁并执行同步代码块。
2. 有序性
有序性指的是代码执行的顺序符合程序设计的逻辑顺序。在java的内存模型中,编译器和处理器为了优化性能,可能会对指令进行重排序,这种重排序不会改变单线程内程序的语义,但可能影响多线程下的语义正确性。
原理分析:
- 内存屏障:synchronized内部通过内存屏障指令(如LoadLoad,StoreStore(确保在屏障前的所有存储(写)操作在屏障之后的所有存储操作之前完成,防止写入操作重排,确保写入顺序。)等)来防止指令重排序。在进入同步代码块时,插入LoadLoad(确保在屏障前的所有加载(读)操作在屏障之后的所有加载操作之前完成,防止读取操作重排,确保读取顺序。)、LoadStore(确保在屏障前的所有加载操作在屏障后的所有存储操作之前完成。)等内存屏障,保证在此屏障之前的操作不会被重排到此屏障之后。
- Happens-Before规则:
synchronized
关键字本质上是建立了一种内存的“happens-before”关系,这种关系规定了操作的顺序性。例如,解锁操作unlock
必须在后续的加锁操作lock
之前发生。
3. 可见性
可见性保证了一个线程对共享变量的修改能够及时被其他线程看到。Java 内存模型规定线程在对共享变量进行写操作时,必须将该变量的值刷新到主内存,而线程在读取共享变量时,必须从主内存中重新读取最新的值。
原理分析:
- 主内存与工作内存:Java 内存模型规定每个线程都有自己的工作内存(类似于 CPU 缓存),而共享变量存放在主内存中。
synchronized
保证了在进入同步代码块时,线程必须从主内存中读取最新的共享变量值,而在退出同步代码块时,必须将共享变量的修改刷新到主内存中。 - 内存屏障:在
synchronized
的实现中,会在锁的释放和获取时插入内存屏障。具体来说:- 在释放锁时,插入
StoreStore
屏障,将工作内存中修改的变量值刷新到主内存。 - 在获取锁时,插入
LoadLoad
屏障,确保线程能从主内存中获取最新的数据。
- 在释放锁时,插入