Synchronized原语
synchronized作为JAVA同步的关键原语,其作用范围可以分为三类:
// sync 原语 作用范围publicclass SynchronizedSample {privatefinal Object lock = new Object();privatestaticint money = 0;//非静态方法public synchronized void noStaticMethod(){money++;}//静态方法public static synchronized void staticMethod(){money++;}public void codeBlock(){//代码块synchronized (lock){money++;}}}
| 作用范围 | 锁对象 |
|---|---|
| 非静态方法 | 当前对象 => this |
| 静态方法 | 类对象 => SynchronizedSample.class (一切皆对象,这个是类对象) |
| 代码块 | 指定对象 => lock (以上面的代码为例 |
在JDK1.6之前synchronized属于重量级锁,主要依赖os的mutex lock实现,涉及到线程从用户态到内核态的切换,切换开销较大。在1.6后,引入了偏向锁和轻量级锁,提升了synchronized的性能。
预备知识
JAVA的对象结构主要如下。
其中对象头分为:
| 对象头结构 | 存储信息-说明 |
|---|---|
| Mard Word | 存储对象的hashCode、锁信息或分代年龄或GC标志等信息 |
| Klass Word | 存储指向对象所属类(元数据)的指针,JVM通过这个确定这个对象属于哪个类 |
除此之,每个对象都有一个关联的monitor对象,其属性如下:
//👇图详细介绍重要变量的作用ObjectMonitor() {_header = NULL;_count = 0; // 重入次数_waiters = 0, // 等待线程数_recursions = 0;_object = NULL;_owner = NULL; // 当前持有锁的线程_WaitSet = NULL; // 调用了 wait 方法的线程被阻塞 放置在这里_WaitSetLock = 0 ;_Responsible = NULL ;_succ = NULL ;_cxq = NULL ;FreeNext = NULL ;_EntryList = NULL ; // 等待锁 处于block的线程 有资格成为候选资源的线程_SpinFreq = 0 ;_SpinClock = 0 ;OwnerIsThread = 0 ;}
对于ObjectMonitor对象内部有一个竞争锁的机制,其原理如下:
当有两个线程A,B同时访问synchronized锁时,A抢先拿到了锁对象,这时monitor会把_owner设置为A,将对象的markword设置为Monitor,锁标记为10。
随后B会被加入waiting queue队列中被阻塞。
JVM每个冲Waiting Queue取一个元素到OnDesk作为候选者。为了防止Waiting Queue被大量线程执行CAS,单独设置了EntryList作为有资格进入OnDesk。而OnDesk作为竞争锁资源的线程,最多只能有一个。处于waiting queue和blocking queue的线程均处于阻塞状态。这种synchronize的锁是非公平的。
Synchronized优化
在1.6后java引入了偏向锁和轻量级锁。通过这些措施来优化了synchronized的加锁开销。
