volatile关键字:
可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。
synchronized关键字:
可以修饰方法或以同步块的形式来进行使用,它主要确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中,它保证了对变量访问的可见性和排他性。
package com.baidu.nuomi.concurrent; /** * Created by sonofelice on 16/6/18. */ public class Synchronized { public static void main(String[] args) { synchronized (Synchronized.class){ } } public static synchronized void m(){} }
使用javap查看生成的class文件:
在该类的同级目录下执行javap -v Synchronized.class
得到下面的结果:
Classfile /Users/sonofelice/Downloads/zhmmself/target/classes/com/baidu/nuomi/concurrent/Synchronized.class Last modified 2016-6-18; size 590 bytes MD5 checksum 64550d9817510bcc5d531e20e814b122 Compiled from "Synchronized.java" public class com.baidu.nuomi.concurrent.Synchronized SourceFile: "Synchronized.java" minor version: 0 major version: 50 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #3.#22 // java/lang/Object."<init>":()V #2 = Class #23 // com/baidu/nuomi/concurrent/Synchronized #3 = Class #24 // java/lang/Object #4 = Utf8 <init> #5 = Utf8 ()V #6 = Utf8 Code #7 = Utf8 LineNumberTable #8 = Utf8 LocalVariableTable #9 = Utf8 this #10 = Utf8 Lcom/baidu/nuomi/concurrent/Synchronized; #11 = Utf8 main #12 = Utf8 ([Ljava/lang/String;)V #13 = Utf8 args #14 = Utf8 [Ljava/lang/String; #15 = Utf8 StackMapTable #16 = Class #14 // "[Ljava/lang/String;" #17 = Class #24 // java/lang/Object #18 = Class #25 // java/lang/Throwable #19 = Utf8 m #20 = Utf8 SourceFile #21 = Utf8 Synchronized.java #22 = NameAndType #4:#5 // "<init>":()V #23 = Utf8 com/baidu/nuomi/concurrent/Synchronized #24 = Utf8 java/lang/Object #25 = Utf8 java/lang/Throwable { public com.baidu.nuomi.concurrent.Synchronized(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/baidu/nuomi/concurrent/Synchronized; public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: ldc_w #2 // class com/baidu/nuomi/concurrent/Synchronized 3: dup 4: astore_1 5: monitorenter 6: aload_1 7: monitorexit 8: goto 16 11: astore_2 12: aload_1 13: monitorexit 14: aload_2 15: athrow 16: return Exception table: from to target type 6 8 11 any 11 14 11 any LineNumberTable: line 8: 0 line 10: 6 line 11: 16 LocalVariableTable: Start Length Slot Name Signature 0 17 0 args [Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 11 locals = [ class "[Ljava/lang/String;", class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4 public static synchronized void m(); flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 12: 0 }
分析一下synchronized关键字的实现细节。
上面class信息中,对于同步块的实现使用了
monitorenter 和 monitorexit
指令。而同步方法则是依靠方法修饰符上的
ACC_SYNCHRONIZED
来完成的。
无论哪种方式,其本质是对一个对象的监视器进行获取,而这个获取的过程是排他的,也就是同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。