1.概念
synchronized 是利用锁的机制来实现同步的,而锁的机制有两种特性:
a.互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程种的协调机制,这样在同一时间内只有一个线程对需同步的代码块进行访问,互斥操作往往也被叫做原子操作,具有原子性.
b.可见性:必须确保在锁被释放之前,对共享变量锁做的修改,对于随后的获得该锁的另一个线程是可见的,可见底层是采用内存屏障去强行刷到主存或者高速缓存中的.
2.synchronized的用法
1.同步方法
Public synchronized void methodName(){
//业务逻辑
}
2.同步代码块
synchronized(this or object or .class) {}
3.synchronized底层原理实现
在java中,当方法或者类或者代码块使用了sychronized的时候,每个对象都会创建一个monitor对象,这个对象称为"内置锁"或者"对象锁".
在加入synchronized的时候在JVM层面其实是加了Monitorenter指令和Monitorexit指令
在monitorenter指令之后,Load屏障会加一个Acquire屏障,这个屏障可以禁止读操作和写操作之间发生指令重排序,这就可以保证sychronized的有序性,内部可以重排序,但无法和外部指令发生重排序,还会进行一个refresh操作,保证数据的可见性.
在monitorexit指令之后,会有一个store屏障,他会让线程把自己在同步中改变的变量都执行flush操作,把变量刷到高速缓存,或者主内存中去.
两个指令可以使得synchronized拥有可见性和有序性.
对象在堆里面被创建是内存中的布局分为 对象头 ,实例数据和对齐填充.
对象头一般包含两个信息:
1.自身的运行时数据:如 锁的状态,线程持有锁 这部分内容一般叫Mark Word
2.类型指针:JVM通过这个指针来确定对象是哪个类的实例.
上图就是一个完整的synchronized原理图
1.对象实例创建的时候会在对象头创建一个markWord区域,里面就包含了ObjectMonitor对象的指针.
2.ObjectMonitor对象包含 count (计数器),owner(属于哪个线程调用),waiset(需要被唤醒的线程集合),waitSetLock(唤醒后进行加锁),entryList(等待加锁的线程集合)
调用链路: 当多个线程去调用一个synchronized方法时,首先会保存在entryList中,接下来利用CAS(乐观锁比较替换)对计数器count进行比较看当前数值是否为0 , 如果当前数值为0 则加锁成功,owner就会记录当前线程属于哪个线程调用,如果调用了wait方法则进入waitset线程集合中等待.
并且synchronized是可重入锁,相同的线程可以重复进入 每进入一次计数器+1,出去则-1.
正是这种加锁 释放锁的过程使得ObjectMonitor整个流程是原子性的.要么加锁成功 要么加锁失败.