因为锁是存储在对象头里面,所以我们先看下对象的存储结构。
锁是记录在对象头信息中的, Synchronized 修饰的进行加锁的过程
请看代码:
public class TestMain { 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()); } } }
输出结果:
发现加锁之后对象头信息发生了变化,证明所所信息是存储到对象头中的。
使用 synchronized 关键字不是一开始就是加重量级锁的,是根据情况一步步升级的,不同级别对系统的资源小号是不一样的,只有必要情况才会升级到重量级锁 “Synchronized” 的,以下是锁升级的过程图。
锁升级过程: new - 偏向锁-轻量级锁(无锁、自旋锁、自适应锁)- 重量级锁
通过 new 关键字创建对象
升级过程:
线程抢夺锁:
markwork 存储值:
升级原因:
升级策略:
升级过程:
markwork 存储值:
来自于马士兵
public void add(String str,String str2){ StringBuffer sb = new StringBuffer(); sb.append(str).append(str2); }
StringBuffer 是线程安全的,因为它的关键方法都是被 synchronized 修饰过的,但是发现上面的代码,sb 这个引用只会在 add 方法中使用,不可能被其他线程引用 (因为是局部变量,栈私有),因此 sb 是不可能共享的资源,JVM 会消除 StringBuff 对象内部的锁。
public void test() { int i = 0; //在锁粗化之前运行逻辑如下列代码 while(i < 100) { synchronized (this) { } i++; } //在锁粗化之后运行逻辑如下列代码 synchronized (this) { while(i < 100) { i++; } } }
JVM 会检测到这样一连串的操作都对同一个对象加锁(while 循环内 100 次执行 append ,没有锁粗化的就要进行 100 次加锁/解锁 ),此时 JVM 就会将加锁的范围粗化到这一连串的操作的外部(比如 while 循环体外),使得这一连串操作只需要加一次锁即可。
首先了解一下 JVM 的即时编译,对于热点代码直接编译成机器语言,提高效率。
JIT 会把热点代码编译成汇编语言
public class T2 { public static void main(String[] args) { for (int i = 0; i < 1000000; i++) { m(); n(); } } public static synchronized void m(){ } public static void n(){ } }
m() , n() 执行了一百万次,所以这两个方法在这个程序中就叫热点代码,把热点代码进行即时编译,编译成机器语言,到第10次编译成机器语言了,可能到第11次执行的时候就不再重新解释了,就直接执行了,效率会高很多。
通过命令查看:
java -XX:+UnlockDiagnosticVMOptions -XX:PrintAssembly T2
C1 Compile Level 1 (一级优化)
C2 Compile Level 2 (二级优化)
JIT 会把 m(),n() 编译成汇编码,会看到 lock comxchg …指令。
变量。。。。 跟自己线程绑定的。
创建 ThreadLocal 线程
public void Test03(){ ThreadLocal t = new ThreadLocal(); t.set(new Object()); t.remove(); //防止内存泄漏 }
查看 set 方法代码
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); } //getMap(t); ThreadLocalMap getMap(Thread t) { return t.threadLocals; } // t.threadLocals; ThreadLocal.ThreadLocalMap threadLocals = null;