王安安的记录 2024-06-16 12:51 采纳率: 20%
浏览 3

volatile禁止重排序


public class OutOfOrderDemo {
    private volatile static int a=0,b=0;
    public volatile static int i=0,j=0;
    public static void main(String[] args) throws InterruptedException {
        int count=0;
        while(true)
        {
            a=0;b=0;
             i=0;j=0;
            count++;
            Thread t1=new Thread(new Runnable() {
                @Override
                public void run() {
                    a=1;
                    i=b;
                }
            });
            Thread t2=new Thread(new Runnable() {
                @Override
                public void run() {
                    b=1;
                    j=a;
                }
            });
//            1和t2不一定会按照它们在代码中编写的顺序执行。这是因为在多线程环境中,JVM不保证指令的顺序和执行顺序,除非显式地使用同步操作来确保顺序性。
//            因此,t1和t2可能会以不同的顺序执行其操作,这可能会导致意外的结果。
            t1.start();

            t1.join();//t1优先执行完毕,优先结束
            t2.start();//这样测试发现没有出现i=1,j=1的
            t2.join();

            System.out.println("i="+i+"j="+j);
            if( i==0&&j==1)
            {
                System.out.println("正常"+count);
            }
            else {
                System.out.println("不正常============="+count+";"+i+"  "+j);
                break;
            }

        }
    }
}


这样跑出来目前没发现有问题。
但是按理来说是有可能出问题的:t1,t2之间的执行顺序会有可能颠倒之类
volatile禁止重排序针对的是变量的赋值,但是不能保证现成的执行顺序造成的i=0,j=1之外的情况(之前出现了i=1,j=0)不知道这么理解对不对?
单线程按顺序执行,会出现这样的指令重排序造成的问提嘛?


  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-06-16 12:51
    关注

    引自免费微信小程序:皆我百晓生

    关于你提出的volatile关键字与重排序的问题,理解上有一些偏差。volatile确实可以防止某些类型的重排序,但它主要是为了确保线程间变量的可见性和禁止特定类型的编译器优化,而不是完全控制所有可能的指令重排序。下面我将详细解释,并提供一个更深入的分析。

    volatile与重排序

    volatile关键字的主要作用有两个:

    1. 可见性:当一个线程修改了volatile变量后,其他线程可以立即看到这个变化。
    2. 禁止某些重排序volatile读和写之间形成了一个内存屏障,这阻止了编译器和处理器对这些操作进行重排序。

    然而,volatile并不能完全阻止所有类型的重排序。它主要阻止的是那些可能导致数据竞争条件的重排序类型。例如,它可以阻止编译器将volatile变量的读/写操作与其他非volatile操作重排序,但这并不意味着它可以阻止所有可能的线程执行顺序变化。

    代码示例分析

    在你提供的代码中,尽管a, b, i, 和 j 都被声明为volatile,但volatile的特性并不足以确保t1t2线程中的操作顺序不会发生重排序。这是因为volatile只影响到与volatile变量相关的读写操作,而不能控制整个方法或线程的执行顺序。

    在多线程环境下,线程调度由操作系统决定,这意味着t1t2可能不会严格按照代码中定义的顺序执行。然而,在你当前的代码中,由于t1线程先启动并等待结束(t1.join())后才启动t2线程,这实际上创建了一个隐式的执行顺序,即t1先于t2执行完成。因此,在这个特定的上下文中,你观察到的执行顺序可能符合预期。

    如何确保线程执行顺序?

    如果你想要确保t1t2之间的操作顺序,而不仅仅是volatile变量的读写顺序,你需要使用更强大的同步机制,如LockSemaphoreCountDownLatch等。例如,使用CountDownLatch来确保t1t2之间的依赖关系:

    import java.util.concurrent.CountDownLatch;
    
    public class OutOfOrderDemo {
        private volatile static int a = 0, b = 0;
        public volatile static int i = 0, j = 0;
        public static void main(String[] args) throws InterruptedException {
            CountDownLatch latch = new CountDownLatch(1);
    
            Thread t1 = new Thread(() -> {
                a = 1;
                latch.countDown();
                try {
                    latch.await(); // 等待t2完成初始化b
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i = b;
            });
    
            Thread t2 = new Thread(() -> {
                try {
                    latch.await(); // 等待t1完成初始化a
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                b = 1;
                latch.countDown();
                j = a;
            });
    
            t1.start();
            t2.start();
    
            t1.join();
            t2.join();
    
            System.out.println("i=" + i + " j=" + j);
        }
    }
    

    在这个版本中,CountDownLatch用于确保t1t2之间的操作顺序。latch的计数器初始值设为1,t1t2都必须调用await()来等待对方完成初始化变量。这样可以确保t1设置a之后t2才能设置b,反之亦然。

    单线程环境下的重排序

    在单线程环境中,由于不存在线程切换,因此不需要担心线程间的可见性问题。然而,即使是在单线程中,编译器和处理器也可能会进行重排序以优化性能。volatile可以阻止那些可能破坏程序语义的重排序,但在单线程环境下,除非有明确的性能需求,否则通常不需要过分关注重排序的影响。

    希望这能帮助你更深入地理解volatile的作用以及如何在多线程环境中正确使用它来确保数据一致性。如果有更多问题或需要进一步的解释,请随时提问。

    评论 编辑记录

报告相同问题?

问题事件

  • 创建了问题 6月16日

悬赏问题

  • ¥15 jetson nano
  • ¥15 :app:debugCompileClasspath'.
  • ¥15 windows c++内嵌qt出现数据转换问题。
  • ¥20 公众号如何实现点击超链接后自动发送文字
  • ¥15 用php隐藏类名和增加类名
  • ¥15 算法设计与分析课程的提问
  • ¥15 用MATLAB汇总拟合图
  • ¥15 智能除草机器人方案设计
  • ¥15 对接wps协作接口实现消息发送
  • ¥15 SQLite 出现“Database is locked” 如何解决?