多线程环境中的易失性与静态变量

时间:2017-06-23 08:22:52

标签: java multithreading static volatile

我正在学习多线程,我发现了一些我无法解释的有趣内容。据我所知,如果两个线程正在访问一个静态变量,他们可以将自己的副本放入其缓存中。 Thread1对其本地缓存中的静态变量进行的更新不会反映在Thread2缓存的静态变量中。

出于这个原因,我在Cracker.java中的isFound静态变量应该是静态的和易变的,但它并不重要,因为当这个退出条件设置为true时,所有线程立即停止。有人可以向我解释一下吗?

HashDecryptor.java

 public class HashDecryptor {    

        private List<Thread> threads = new ArrayList<>();
        // some other fields

        public HashDecryptor() {            
            createThreads();
        }

        private void createThreads() {
            long max = (long) (Math.pow(26, numberOfChars));
            int n = numberOfThreads;
            for (int i = 0; i < n; ++i) {
                if (i == 0) {
                    threads.add(new Thread(new Cracker(hashToDecrypt, (max * i / n), (max * (i + 1) / n))));
                } else {
                    threads.add(new Thread(new Cracker(hashToDecrypt, (max * i / n) + 1, (max * (i + 1) / n))));
                }
            }
        }

        public void startDecryting() {
            for (Thread t : threads) {
                t.start();
            }
        }

    }

Cracker.java

public class Cracker implements Runnable {

    // Some other fields

    private static boolean isFound;

    public Cracker(String hashToDecrypt, long start, long end) {
        this.hashToDecrypt = hashToDecrypt;
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {
        decrypt();
    }

    public void decrypt() {
        LocalTime startTime = LocalTime.now();
        long counter = start;
        while (!isFound && counter <= end) {
            if (match(counter)) {
                isFound = true;
                printData(generatePassword(counter), startTime);
            }
            counter++;
        }
    }   

}

2 个答案:

答案 0 :(得分:0)

  

由于这个原因,我在Cracker.java中的isFound静态变量应该是静态的和易变的,但是没关系,因为当这个退出条件设置为true时,所有线程立即停止。有人可以向我解释一下吗?

您可以通过多种方式获得可能导致此问题的偶然同步。首先,您的应用程序可能正在与硬件上运行的其他应用程序竞争CPU资源,并且应用程序可能会被换出。也许你有比你有更多的线程。当线程被换出时,这两者都可能导致脏内存刷新到核心内存。

另一种可能的情况是,您的线程正在跨越其他内存障碍,例如调用其他synchronized方法或访问其他volatile字段。例如,我想知道这个语句,因为一些输入/输出流具有同步类。

printData(generatePassword(counter), startTime);

您可能会尝试删除数据的打印,以查看您的应用程序行为是否发生了变化。

  

我告诉你它运行正常,我确实用sysout验证它。这就是奇怪的事情,这就是我问这个问题的原因:)

完美的例子。 System.outPrintStream,它是synchronized类,因此调用println()将导致您的线程跨越读写内存屏障,这将更新您的{{1} }字段。重要的是要注意任何内存屏障会影响缓存内存的所有。跨越任何读取内存屏障会强制从中央内存更新所有缓存的内存。跨越任何写入内存屏障会强制将所有本地脏内存写入中央。

问题是当您删除static方法或应用程序停止调用System.out类,然后synchronized变量未正确更新时。所以你不能依赖它,但它确实发生了。

答案 1 :(得分:0)

静态变量:在Object的上下文中使用,其中由一个对象进行的更新将反映在同一个类的所有其他对象中但不在Thread的上下文中,其中一个线程更新为静态变量将反映更改立即到所有线程(在其本地缓存中)。  如果两个线程(假设t1和t2)正在访问同一个对象并更新一个声明为static的变量,那么这意味着t1和t2可以在它们各自的缓存中创建相同对象(包括静态变量)的自己的本地副本,所以由t1对其本地缓存中的静态变量进行的更新不会反映在t2缓存的静态变量中。

易失性变量:如果两个线程(假设t1和t2)正在访问同一个对象并更新一个声明为volatile的变量,则意味着t1和t2可以创建自己的本地缓存除了声明为volatile的变量之外的对象。所以volatile变量只有一个主副本,它将由不同的线程更新,一个线程对volatile变量的更新将立即反映到另一个Thread。