synchronized简介
众所周知Java使用synchronized关键字实现多线程环境下同步语义,为什么会出现线程安全,会产生什么影响?使用synchronized可以自动帮我们将多线程执行逻辑变为串行执行,保证同一时刻仅有一个线程可以执行synchronized所保护的代码逻辑
synchronized在多线程环境下具有以下语义
1.可见性:一个线程对共享变量的修改,其他线程立即可以得知
2.有序性:cpu以及编译器在不改变代码执行结果的情况下,为了提升执行效率,会对代码进行乱序执行(指令重排序),在单线程下是不会出现任何问题,但是在多线程执行,可能出现意外的结果,有序性是指禁止指令重排序,按照代码原有的逻辑进行执行
3.原子性:对共享变量的修改过程不允许被打断
synchronized使用的三种方式:
1.锁定普通方法,锁住的是当前实例对象
public class SynchronizedMethod {
public static void main(String[] args) {
SynchronizedMethod sm = new SynchronizedMethod();
//锁住的sm实例
sm.read();
}
//在方法上加synchronized 锁住当前实例
public synchronized void read(){
System.out.println("I am SynchronizedMethod");
}
}
2.锁定静态方法,锁住的是当前Class对象
public class SynchronizedStaticMethod {
public static void main(String[] args) {
//锁定当前Class对象
SynchronizedStaticMethod.read();
}
public static synchronized void read() {
System.out.println("I am SynchronizedStaticMethod");
}
}
3.同步方法块,锁定synchronized 后括号中配置的对象(既可以是Class的实例对象也可以是Class对象)
public class SynchronizedCodeBlock {
public void read(){
//锁定当前实例对象
synchronized(this){
System.out.println("I am SynchronizedCodeBlock");
}
//锁定当前Class对象
synchronized(SynchronizedCodeBlock.class){
System.out.println("I am SynchronizedCodeBlock");
}
}
}
JAVA内存模型定义了8种操作来完成主内存和工作内存(线程独有),分别是lock(锁定)、unlock(解锁)、read(读取)、load(载入)、use(使用)、assign(赋值)、store(存储)、write(写入),其中lock和unlock可以实现代码块范围的原子性操作,虚拟机并未直接将lock和unlock操作直接开发给用户使用,但是提供了更高层次的字节码指令monitorenter和monitorexit来隐式使用这两个操作,而monitorenter和monitorexit即为synchronized的实现,因此在synchronized所保护的代码逻辑执行为原子性,当使用synchronized时会将monitorenter和monitorexit字节码分别插入到所保护的代码逻辑的开始和结束
我们使用javap反编译SynchronizedCodeBlock.class得到如下字节码
public SynchronizedCodeBlock();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void read();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter //在控制台输出之前插入monitorenter 指令
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String I am SynchronizedCodeBlock
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit //当执行完逻辑退出 monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
...............
在同一时刻只能有一个线程进入monitorenter直到将monitorexit 执行完成后,在由其他线程竞争进入,保证了同一时刻只有一个线程操作该资源以此实现原子性,并且修改过的共享变量是对其他线程立即可见,也就是实现可见性语义
总结
synchronized属于重量级锁,在保证线程安全的同时也需要付出性能损耗的代价,当然synchronized使用方便,安全性高也是它的优势,在java中还有Lock、CAS等实现线程同步,当然还有轻量级volatile关键字来保证线程安全,当然它们有各自的是用场景,如何选择可以根据具体是用场景来使用,当然只有理解了这些锁的机制,才能正确使用它们,并且能以最低的性能开销来保证线程安全
(转载请注明出处
)