public class ThreadSafe { public static void main(String[] args) { Safe safe = new Safe(); new Thread(new Runnable() { public void run() { try { while (true) { safe.setI(100); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { while (true) { try { safe.getI(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } class Safe { int i; public Safe() { this.i = 0; } public synchronized void setI(int i) throws InterruptedException { System.out.println("setId"+" "+i); Thread.sleep(1000 * 3); System.out.println("backsetId"+" "+100); this.i = i; } public synchronized int getI() throws InterruptedException { System.out.println("getId"+" "+i); this.i=10; Thread.sleep(1000 * 3); System.out.println("backgetId"+" "+10); return this.i; } }
输出:
setId 100
backsetId 100
getId 100
backgetId 10
setId 100
backsetId 100
setId 100
synchronized在方法声明中,锁的是this,
在jvm汇编指令里面并没有显式的出现monitorenter
下面代码
public class ThreadSafe { public static void main(String[] args) { Safe safe = new Safe(); new Thread(new Runnable() { public void run() { try { while (true) { safe.setI(100); } } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { public void run() { while (true) { try { safe.getI(); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } class Safe { int i; public Safe() { this.i = 0; } public synchronized void setI(int i) throws InterruptedException { System.out.println("setId" + " " + i); Thread.sleep(1000 * 3); System.out.println("backsetId" + " " + 100); this.i = i; } public int getI() throws InterruptedException { synchronized (this) { System.out.println("getId" + " " + i); this.i = 10; Thread.sleep(1000 * 3); System.out.println("backgetId" + " " + 10); } return this.i; } }
执行结果一样
public synchronized void setI(int) throws java.lang.InterruptedException; Code: 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 3: new #4 // class java/lang/StringBuilder 6: dup 7: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 10: ldc #6 // String setId 12: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15: iload_1 16: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 19: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 25: ldc2_w #11 // long 3000l 28: invokestatic #13 // Method java/lang/Thread.sleep:(J)V 31: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 34: ldc #14 // String backsetId 100 36: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 39: aload_0 40: iload_1 41: putfield #2 // Field i:I 44: return public int getI() throws java.lang.InterruptedException; Code: 0: aload_0 1: dup 2: astore_1 3: monitorenter 进入管程 4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 7: new #4 // class java/lang/StringBuilder 10: dup 11: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 14: ldc #15 // String getId 16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: aload_0 20: getfield #2 // Field i:I 23: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 26: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 29: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 32: aload_0 33: bipush 10 35: putfield #2 // Field i:I 38: ldc2_w #11 // long 3000l 41: invokestatic #13 // Method java/lang/Thread.sleep:(J)V 44: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 47: ldc #16 // String backgetId 10 49: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 52: aload_1 53: monitorexit 退出管程 54: goto 62 57: astore_2 58: aload_1 59: monitorexit 60: aload_2 61: athrow 62: aload_0 63: getfield #2 // Field i:I 66: ireturn Exception table: from to target type 4 54 57 any 57 60 57 any
注意有个方法出现了进入管程指令,退出管程指令,有个方法没有
来自java虚拟机规范的解释
Java 虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程( Monitor)来支持的。方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作( §2.11.8)之中。
虚拟机可以从方法常量池中的方法表结构( method_info Structure, §4.6)中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有管程,然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放管程。在方法执行期
间,执行线程持有了管程,其他任何线程都无法再获得同一个管程。如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放
monitor和对象的关系.来自jvm文档
monitor 即可以实现为与对象一同分配
和销毁,也可以在某条线程尝试获取对象所有权时动态生成,在没有任何线程
持有对象所有权时自动释放。
实际上synchoronized关键字修饰方法的时候就是进入对象的monitor,所以第一个代码会出现in-out一组一组的出现,而不会出现in-in2-out这种组合