sychronized
- CAS概念
需求:创建100个线程,每个线程都对共享变量m增加100,100个线程运行结束时,m应该等于10000。
我们针对以上需求,有许多的实现方法,其中java为我们提供了一个原子类AtomicInteger,代码:
public class ViolateAtomic extends Thread{
private static volatile AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count.incrementAndGet();
}
System.out.println("线程"+ Thread.currentThread().getName()+":"+count);
}
public static void main(String[] args) {
ViolateAtomic[] arr = new ViolateAtomic[100];
for (int i = 0; i < 100; i++) {
arr[i] = new ViolateAtomic();
}
for (int i = 0; i < 100; i++) {
arr[i].start();
}
}
}
我们发现代码中并没有使用加锁的明代码,我们深入底层时发现incrementAndGet方法底层调用的compareAndSwap是native方法,即用C++代码实现的
机制:某个线程拿到共享变量E,经过计算得到V,此时再去拿到内存中的共享变量N,比较E和N是否一致,如果一致,则将计算后的V更新到内存中;
如果不一致,则拿到新的E,继续循环(但这种情况下,可能出现ABA问题,加个版本号字段即可解决该问题),具体参考下图
上述即被成为CAS,即无锁或自旋锁,该锁的机制不被操作系统掌控,所以是轻量级锁
- 锁的升级过程
线程开始从无锁转为偏向锁,竞争加剧转为轻量级锁,当线程达到一定数量时经由操作系统管理,此时转为重量级锁
- 对象在内存中的布局
4个部分组成:markword(锁信息,对象信息等)、classpointer、构造方法、补齐字节(总线读取内存中的数据放入寄存器中时,以16个字节读取内容效率更高)
- synchronized
public class App
{
public static void main( String[] args )
{
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable());
}
}
}
加锁前markword第一个字节为00000001,末位为01,此时为无锁态;加锁后为00,此时是轻量级锁,即自旋锁;锁的状态参考下图:
锁状态 | 说明 | 标志位 |
无锁态 | 0(区分是否偏向锁的标志位) 0 1 | |
偏向锁 | 当前线程指针JavaThread | 1(区分是否偏向锁的标志位) 0 1 |
轻量级锁 | 指向线程中LR指针 | 0 0 |
重量级锁 | 指向互斥量的指针 | 1 0 |
GC标记信息 | CMS过程用到的标记信息 | 1 1 |
- 总结
线程创建从无锁转为偏向锁,竞争加剧时,偏向锁转向轻量级锁(自旋锁),此时多个线程会生成自己的LockRecord以CAS的方式改变对象的对象头信息(即markword)进行加锁,即为synchronized的本质