MyObject lock = new MyObject();
synchronized(lock){
}
java对象都是分为对象头和实例对象两块的,其中实例对象就是大家平时看到的那些变量数据,然后对象头包含两块东西,一个是Mark Word(包含hashCode、锁数据、GC数据等等),另一个是Class Metadata Address(包含了指向类的元数据的指针)
在Mark Word里就是一个指针,是指向对象实例monitor的地址,这个monitor是C++实现的,不是java实现的,这个monitor实际上是C++实现的一个ObjectMonitor对象,里面包含了一个_owner指针,指向持有锁的线程。
各个线程尝试竞争加锁,此时竞争加锁是在JDK1.6以后优化成了基于CAS来进行加锁。操作_count计算器,比如说将_count的值尝试从0变为1
ObjectMonitor里还有一个entryList,想要加锁的线程全部进去这个entryList等待机会尝试加锁,实际有机会加锁的线程,就会设置_owner指针指向自己,然后对_count计数器累加1一次
然后释放锁的时候,先是对_count计数器递减,如果为0了就会设置_owner为null,不在指向自己,代表自己彻底释放锁。
如果获取锁的线程执行wait,就会将计数器递减,同时_owner设置为null,然后自己进入waitset中等待唤醒,别人获取了锁执行notify的时候就会唤醒waitset中的线程竞争尝试获取锁。
synchronized保证可见性和有序性的
int b=0;
int c=0;
synchronized(this){ -> monitorenter
Load内存屏障
Acquire屏障
int a=b;
c=1;
} -> monitorexit
Store内存屏障
java的并发技术底层很多对应了内存屏障的使用,包括synchronized,他的底层也是依托于各种不同的内存屏障保证可见性和有序性的。
按照可见性划分的话,内存屏障可以分为Load屏障和Store屏障。
Load屏障的作用是执行refresh处理器缓冲的操作,说白了就是对别的处理器更新过的变量从其他处理器的高速缓冲(或者主内存)加载数据到自己的高速缓冲,确保自己看到的是最新的数据。
Store屏障的作用是执行flush处理器缓冲操作,说白了就是把自己当前处理器更新的值,都刷新到高速缓冲(或者主内存)里去。
在执行monitorexit指令之后,会有一个Store内存屏障,让线程把自己在同步代码块里修改的变量的值都执行flush处理器缓冲操作,刷到高速缓冲(或者主内存)里去,然后在monitorenter指令之后会加一个Load屏障,执行refresh处理器缓冲操作,把别的处理器修改的最新值加载到自己的高速缓冲里来。
按照有序性划分的话,还可以分为Acquire屏障和Release屏障
在monitorenter指令之后,Load屏障之后,会加一个Acquire屏障,这个屏障的作用是禁止读操作和读写操作之间发生指令重排序,在monitorexit指令之前,会加一个Release屏障,这个屏障作用是禁止写操作和读写操作之间发生指令重排序。
synchronized禁止synchronized代码块里的代码指令重排发生。