wushu15092 2021-03-05 11:19 采纳率: 100%
浏览 138
已采纳

Synchronized对实例对象的成员变量同步时是否还会进行代码的重排序

代码如下图所示,代码逻辑主要是启动多个线程,对同一个实例的成员变量进行同步和操作:

public class SynTest {

    private Integer obj = 1;
    /**
     * @param args
     */
    public static void main(String[] args) {
        final SynTest synTest = new SynTest();
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synTest.add();
                }
            }).start();
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
    }
    public void add() {
        synchronized (obj) {
            System.out.println(Thread.currentThread());
            obj++;
            System.out.println(Thread.currentThread() + "===" + obj);
            obj++;
            System.out.println(Thread.currentThread() + "-" + obj);
            obj++;
            System.out.println(Thread.currentThread());
        }
    }
}

运行结果如下:

我的问题点主要是:  

1. 为什么单个线程的操作不是连续的?我理解只有一个线程获取到锁,然后进行操作并输出结果,那么输出结果应该是一个线程输出完,另一个线程才会继续输出

2.JVM会对同步的代码块进行同步消除优化?实际的代码应该是和下图所示?

  • 写回答

5条回答 默认 最新

  • 燕归来兮_ 2021-03-05 12:00
    关注

    1. 为什么单线程操作不是连续的,这是因为虽然你使用了  synchronized (obj) {}  来尝试获取排它锁,但是其他线程把obj修改了,比如从1修改到2 ,又因为Integer使用了享元模式,1 和 2 不是同一个对象,所以你的每个线程可能锁的不是同一个对象,所以也就无法按照你的需求输出,将obj修改为final常量后,各个线程锁定的肯定是同一个对象了,也就可看到连续输出了。

    2. 第二个问题不太熟悉,直接使用 javap -c synTest.class 查看字节码如下,因为 synchronized  进入监视器的字节码指令是 monitorenter ,退出监视器的字节码命令是monitorexit,而这两个命令仅仅出现一次分别是第6行指令和106行指令,所以并没有同步消除优化

    public void add();
        Code:
           0: aload_0
           1: getfield      #3                  // Field obj:Ljava/lang/Object;
           4: dup
           5: astore_1
           6: monitorenter
           7: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
          10: invokestatic  #16                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
          13: invokevirtual #17                 // Method java/lang/Thread.getName:()Ljava/lang/String;
          16: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          19: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
          22: new           #19                 // class java/lang/StringBuilder
          25: dup
          26: invokespecial #20                 // Method java/lang/StringBuilder."<init>":()V
          29: invokestatic  #16                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
          32: invokevirtual #17                 // Method java/lang/Thread.getName:()Ljava/lang/String;
          35: invokevirtual #21                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          38: ldc           #22                 // String ===
          40: invokevirtual #21                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          43: aload_0
          44: getfield      #3                  // Field obj:Ljava/lang/Object;
          47: invokevirtual #23                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
          50: invokevirtual #24                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          53: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          56: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
          59: new           #19                 // class java/lang/StringBuilder
          62: dup
          63: invokespecial #20                 // Method java/lang/StringBuilder."<init>":()V
          66: invokestatic  #16                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
          69: invokevirtual #17                 // Method java/lang/Thread.getName:()Ljava/lang/String;
          72: invokevirtual #21                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          75: ldc           #25                 // String -
          77: invokevirtual #21                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          80: aload_0
          81: getfield      #3                  // Field obj:Ljava/lang/Object;
          84: invokevirtual #23                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
          87: invokevirtual #24                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          90: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
          93: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
          96: invokestatic  #16                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
          99: invokevirtual #17                 // Method java/lang/Thread.getName:()Ljava/lang/String;
         102: invokevirtual #18                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         105: aload_1
         106: monitorexit
         107: goto          115
         110: astore_2
         111: aload_1
         112: monitorexit
         113: aload_2

     更多Java并发编程的学习笔记欢迎访问  https://www.zhoutao123.com/page/book/11

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 fluent的在模拟压强时使用希望得到一些建议
  • ¥15 STM32驱动继电器
  • ¥15 Windows server update services
  • ¥15 关于#c语言#的问题:我现在在做一个墨水屏设计,2.9英寸的小屏怎么换4.2英寸大屏
  • ¥15 模糊pid与pid仿真结果几乎一样
  • ¥15 java的GUI的运用
  • ¥15 Web.config连不上数据库
  • ¥15 我想付费需要AKM公司DSP开发资料及相关开发。
  • ¥15 怎么配置广告联盟瀑布流
  • ¥15 Rstudio 保存代码闪退