synchronized
- java语言级的支持,1.6之后性能极大提高
- 字节码层面的实现: monitorenter/monitorexit
//代码
private void increment(){
synchronized(this){
counter++;
}
}
//字节码
private increment()V
TRYCATCHBLOCK L0 L1 L2 null
TRYCATCHBLOCK L2 L3 L2 null
L4
LINENUMBER 22 L4
ALOAD 0
DUP
ASTORE 1
MONITORENTER //占有锁(监视器)
L0
LINENUMBER 23 L0
ALOAD 0
GETFIELD com/Coordinator$MyThread.i : Ljava/lang/Integer;
ASTORE 2
ALOAD 0
ALOAD 0
GETFIELD com/Coordinator$MyThread.i : Ljava/lang/Integer;
INVOKEVIRTUAL java/lang/Integer.intValue ()I
ICONST_1
IADD
INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
DUP_X1
PUTFIELD com/Coordinator$MyThread.i : Ljava/lang/Integer;
ASTORE 3
ALOAD 2
POP
L5
LINENUMBER 24 L5
ALOAD 1
MONITOREXIT // 退出锁(监视器)
L1
GOTO L6
L2
FRAME FULL [com/Coordinator$MyThread java/lang/Object] [java/lang/Throwable]
ASTORE 4
ALOAD 1
MONITOREXIT
L3
ALOAD 4
ATHROW
L6
LINENUMBER 25 L6
FRAME CHOP 1
RETURN
L7
LOCALVARIABLE this Lcom/Coordinator$MyThread; L4 L7 0
MAXSTACK = 3
MAXLOCALS = 5
//方法2.synchronized 在方法上
private synchronized static void doSomething(){
i++;
}
- 锁住的是什么?
- 在方法中时,锁住的是其中的对象 ;
- 在方法上时,1. static方法锁住的是当前的class对象;2. 非static方法,锁住的是当前对象的实例;
// 反例:
static class MyThread extends Thread{
private synchronized void doSomething(){
}
@Override
public void run(){
doSomething();
System.out.println(123);
}
}
public static void main(String[] args) {
// 开启了四个线程,但synchronized 方法锁住的是每个实例自己的doSomething();所以synchronized 锁并没有用;
new MyThread().start();
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
-
底层实现
- 对象头
- 无锁 -> 偏向锁(biased lock) -(锁膨胀)> 轻量级锁 -(锁膨胀)> 重量级锁
无锁状态: 没有线程访问,是个游离在JVM中的对象;
偏向锁状态: 一直以来都是同一个线程访问,该对象头会变成偏向锁,下次该线程访问,可以直接使用;此时没有锁发生,所以即使线程不再调用,标志位还会一直存在在对象头中;
当第二个线程,尝试获取该锁,偏向锁标志位会撤销,会恢复成无锁的状态;
轻量级锁状态: 有线程竞争的情况发生,但是不严重(同一时刻只有一个线程获取锁),假如能抢到我的话,就不需要获取monitor.
重量级锁状态: 有多个线程在竞争锁,所以必须获取monitor;所以进入monitorenter时,或退出monitorexit时,不一定需要获取monitorenter或释放monitorexit; 对象头定义: markOop.hpp 锁膨胀定义: synchronizer.cpp
锁粗化与锁消除
- 锁粗化: 同一个锁要连续频繁加锁解锁,粗化为更大范围的锁;
public static void main(String[] args) {
increment();
}
private static void increment(){
// monitorenter
for (int i = 0 ;i <10; i++) {
foo();
}
// monitorexit
}
// foo()频繁的加锁解锁,编译器就会把他优化成更大范围的锁
private static synchronized void foo() {
System.out.println("");
}
- 锁消除: 不可能有人和我竞争锁,所以没必要上锁;
网友评论