Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多线程中内存一致性错误的真实示例?_Java_Multithreading - Fatal编程技术网

Java 多线程中内存一致性错误的真实示例?

Java 多线程中内存一致性错误的真实示例?,java,multithreading,Java,Multithreading,在java多线程技术中,它给出了大量内存一致性错误。但我无法复制它。是否有其他方法来模拟内存一致性错误 本教程中提供的示例如下: 假设定义并初始化了一个简单的int字段: int counter = 0; 计数器字段在两个线程A和B之间共享。假设线程A递增计数器: counter++; 然后,不久之后,线程B打印出计数器: System.out.println(counter); 如果这两条语句是在同一个线程中执行的,则可以安全地假定打印出的值为“1”。但是,如果这两条语句在不同的线程中执

在java多线程技术中,它给出了大量内存一致性错误。但我无法复制它。是否有其他方法来模拟内存一致性错误

本教程中提供的示例如下:

假设定义并初始化了一个简单的int字段:

int counter = 0;
计数器字段在两个线程A和B之间共享。假设线程A递增计数器:

counter++;
然后,不久之后,线程B打印出计数器:

System.out.println(counter);
如果这两条语句是在同一个线程中执行的,则可以安全地假定打印出的值为“1”。但是,如果这两条语句在不同的线程中执行,那么打印出来的值很可能是“0”,因为不能保证线程A对计数器的更改对线程B是可见的,除非程序员在这两条语句之间建立了“先发生后发生”关系


这可能会重现问题,至少在我的计算机上,我可以在一些循环之后重现它

  • 假设您有一个
    计数器
    类:

    class Holder {
        boolean flag = false;
        long modifyTime = Long.MAX_VALUE;
    }
    
  • 线程A
    标志设置为
    ,并将时间保存到
    modifyTime
  • 让另一个线程,比如说
    thread\u B
    ,读取
    计数器的
    标志。如果
    thread_B
    即使在
    modifyTime
    之后仍然得到
    false
    ,那么我们可以说我们重现了问题
  • 示例代码

    class Holder {
        boolean flag = false;
        long modifyTime = Long.MAX_VALUE;
    }
    
    public class App {
    
        public static void main(String[] args) {
            while (!test());
        }
    
        private static boolean test() {
    
            final Holder holder = new Holder();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                        holder.flag = true;
                        holder.modifyTime = System.currentTimeMillis();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
            long lastCheckStartTime = 0L;
            long lastCheckFailTime = 0L;
            while (true) {
                lastCheckStartTime = System.currentTimeMillis();
                if (holder.flag) {
                    break;
                } else {
                    lastCheckFailTime = System.currentTimeMillis();
                    System.out.println(lastCheckFailTime);
                }
            }
    
            if (lastCheckFailTime > holder.modifyTime 
                    && lastCheckStartTime > holder.modifyTime) {
                System.out.println("last check fail time " + lastCheckFailTime);
                System.out.println("modify time          " + holder.modifyTime);
                return true;
            } else {
                return false;
            }
        }
    }
    
    结果

    last check time 1565285999497
    modify time     1565285999494
    

    这意味着
    thread\u B
    get
    false
    来自
    计数器
    在时间
    15658999497
    上的
    标志
    ,即使
    thread\u A
    在时间
    15658599994
    上也将其设置为
    true
    (提前3毫秒)。

    所使用的示例太糟糕,无法说明内存一致性问题。要让它工作起来,需要脆弱的推理和复杂的编码。然而,你可能看不到结果。多线程问题是由于计时不正确而导致的。如果有人想增加观察问题的机会,我们需要增加不吉利时机的机会。 下面的程序实现了它

    public class ConsistencyIssue {
    
        static int counter = 0;
    
        public static void main(String[] args) throws InterruptedException {
            Thread thread1 = new Thread(new Increment(), "Thread-1");
            Thread thread2 = new Thread(new Increment(), "Thread-2");
            thread1.start();
            thread2.start();
    
            thread1.join();
            thread2.join();
    
            System.out.println(counter);
        }
    
        private static class Increment implements Runnable{
    
            @Override
            public void run() {
                for(int i = 1; i <= 10000; i++)
                    counter++;
            }
    
        }
    }
    
    公共类一致性问题{
    静态整数计数器=0;
    公共静态void main(字符串[]args)引发InterruptedException{
    螺纹1=新螺纹(新增量(),“螺纹1”);
    螺纹2=新螺纹(新增量(),“螺纹2”);
    thread1.start();
    thread2.start();
    thread1.join();
    螺纹2.连接();
    系统输出打印项次(计数器);
    }
    私有静态类增量实现可运行{
    @凌驾
    公开募捐{
    对于(inti=1;i2++->3

    我们需要一种方法使所有3个步骤原子化,即一次只由一个线程执行

    解决方案1:已同步 用Synchronized环绕增量。因为计数器是静态变量,所以需要使用类级同步

    @Override
            public void run() {
                for (int i = 1; i <= 10000; i++)
                    synchronized (ConsistencyIssue.class) {
                        counter++;
                    }
            }
    
    @覆盖
    公开募捐{
    
    对于(inti=1;i我刚才回答了一个关于Java5中一个bug的问题

    鉴于这段代码:

    public class Test {
        volatile static private int a;
        static private int b;
    
        public static void main(String [] args) throws Exception {
            for (int i = 0; i < 100; i++) {
                new Thread() {
    
                    @Override
                    public void run() {
                        int tt = b; // makes the jvm cache the value of b
    
                        while (a==0) {
    
                        }
    
                        if (b == 0) {
                            System.out.println("error");
                        }
                    }
    
                }.start();
            }
    
            b = 1;
            a = 1;
        }
    }
    
    公共类测试{
    易失性静态私有inta;
    静态私有intb;
    公共静态void main(字符串[]args)引发异常{
    对于(int i=0;i<100;i++){
    新线程(){
    @凌驾
    公开募捐{
    int tt=b;//使jvm缓存的值为b
    while(a==0){
    }
    如果(b==0){
    System.out.println(“错误”);
    }
    }
    }.start();
    }
    b=1;
    a=1;
    }
    }
    
    a
    的易失性存储发生在
    b
    的正常存储之后。因此,当线程运行并看到
    a!=0
    时,由于JMM中定义的规则,我们必须看到
    b==1


    JRE中的错误允许线程进入
    错误
    行,并随后得到解决。如果没有将
    a
    定义为
    volatile

    ,这肯定会失败有时当我试图重现一些真实的并发问题时,我会使用调试器。 在打印上做一个断点,在增量上做一个断点,然后运行整个过程。 以不同的顺序释放断点会得到不同的结果


    可能很简单,但对我来说很有效。

    请再看一看如何在源代码中引入该示例

    <> P>避免内存一致性错误的关键是理解关系发生之前的关系。这种关系仅仅是一个特定语句的内存写入对另一个特定语句可见的保证。 此示例说明了多线程不是确定性的,即不能保证不同线程的操作的执行顺序,这可能会导致在多个运行中出现不同的观察结果。但它没有说明内存一致性错误!

    要理解什么是内存一致性错误,首先需要了解内存一致性。最简单的内存一致性模型是由Lamport在1979年提出的。以下是最初的定义

    任何执行的结果都是相同的,就像所有进程的操作都是按某种顺序执行的,每个进程的操作都是按其程序指定的顺序出现在该顺序中一样

    现在,考虑这个示例多线程程序,请从最近一篇关于顺序一致性的研究论文中查看这个图像。它说明了真正的内存一致性错误可能是什么样子。

    public class Test {
        volatile static private int a;
        static private int b;
    
        public static void main(String [] args) throws Exception {
            for (int i = 0; i < 100; i++) {
                new Thread() {
    
                    @Override
                    public void run() {
                        int tt = b; // makes the jvm cache the value of b
    
                        while (a==0) {
    
                        }
    
                        if (b == 0) {
                            System.out.println("error");
                        }
                    }
    
                }.start();
            }
    
            b = 1;
            a = 1;
        }
    }