1.基本使用
synchronized的底层原理:synchronized
是 Java 中的一个关键字,用于控制多线程的访问,确保同一时刻只有一个线程可以进入临界区(被 synchronized
保护的代码块)。
synchronized 的底层实现主要依赖于 JVM 中的 monitor 对象,它是通过对象监视器在对象头中的锁标志位实现的。当一个线程获取到了对象的监视器,那么这个线程就会处于锁定状态,其他尝试获取该监视器的线程将会被阻塞,直到当前线程释放锁。
以下是 synchronized 的使用示例:
public class SynchronizedExample { private int count = 0; public synchronized void increment() { count++; } public static void main(String[] args) { SynchronizedExample example = new SynchronizedExample(); for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { example.increment(); } }).start(); } try { Thread.sleep(1000); // 等待所有线程执行完毕 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(example.count); // 输出结果应为10000 } }
2.Monitor
Monitor被译为监视器,由jvm提供,C++实现。在字节码中想要体现monitor需要借助javap命令查看class字节码。
找到这个类的class文件,在其目录下执行:javap -v SynTest.class 会得到反编译的结果如下:
【重:有两个monitorexit的原因是因为防止锁住的代码抛异常后不能及时释放,所以需要第二个monitorexit】
如果当前monitor的进入数为0时,线程就会进入monitor。synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代码不执行,所以Lock一般在Finally中释放,而synchronize释放锁是由JVM自动执行的。
Monitor的内部具体结构:
- Owner:存储当前获取锁的线程的,只有一个线程可以获取。
- EntryList:关联没有抢到锁的线程,处于Blocked状态的线程。
- WaitSet:关联调用了wait方法的线程,处于waiting的线程。
具体流程:
- 代码进入Synchronized代码块,先让block对象锁关联的monitor,然后判断Owner是否有线程持有。
- 若没有线程持有,则让当前线程持有,表示该线程获取锁成功。
- 有现成持有,则让当前线程进入entryList进行阻塞,如果Owner持有的线程已经释放了,在entryList中的线程去竞争锁的持有权(非公平)
- 若代码调用了wait方法,则会进入waitSet中等待。
3.Synchronized进阶
monitor实现的锁属于重量级的锁,里面涉及了用户态和进程态的切换、进程的上下文切换,成本较高,性能较低。
JDK6引入了两种新型锁:偏向锁和轻量级锁,引入是为了解决在没有多线程竞争或者基本没竞争的场景下因使用传统机制的锁带来的性能开销。
对象的内存结构:
可以通过lock标识,判断锁的等级:
- 后三位是001表示无锁
- 后三位是101表示偏向锁
- 后两位是00表示轻量级锁
- 后两位是10表示重量级锁
4.锁对比
- monitor重量级锁:每个对象的对象头都可以设置monitor指针,让对象与之产生关联。里面涉及了用户态和进程态的切换、进程的上下文切换,成本较高,性能较低。
- 轻量级锁:在没有竞争时,每次重入仍然需要执行CAS操作,保证原子性。线程加锁的时间是错开的,可以用轻量级锁优化,其修改了对象头的锁标志。
- 偏向锁:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID本身就表示没有竞争,不用重新CAS。以后只要不发生竞争,这个对象就归该线程所有。
CAS(Compare And Swap):比较再交换,体现的是一种乐观锁的思想,在无锁的情况下保证线程操作共享数据的原子性。JUC包下实现的很多类都用上了CAS【AQS框架、AtomicXXXX类】
AQS:AbstractQueuedSynchronizer(队列同步器),AQS定义了一套多线程访问共享资源 的同步器框架,是一个依赖状态(state)的同步器。
- CAS是基于乐观锁的思想:最乐观的估计,不怕别的线程来修改共享变量,就算改了也没关系。
- Synchronized是基于悲观锁的思想:最悲观的估计,防止其他线程来修改共享变量,上了锁后不许修改,释放后才能修改。