Volatile против статической переменной в многопоточной среде

В настоящее время я изучаю многопоточность и нашел что-то интересное, что не могу объяснить. Насколько мне известно, если два потока обращаются к статической переменной, они могут создавать свои собственные копии в своем кэше. Обновление, выполненное Thread1 для статической переменной в ее локальном кэше, не будет отражаться в статической переменной для кэша Thread2.

По этой причине моя статическая переменная isFound в Cracker.java должна быть статической и изменчивой, но это не имеет значения, потому что все потоки немедленно останавливаются, когда для этого условия выхода установлено значение 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 ответа

По этой причине моя статическая переменная isFound в Cracker.java должна быть статической и изменчивой, но это не имеет значения, потому что все потоки немедленно останавливаются, когда для этого условия выхода установлено значение true. Может кто-то объяснить это мне?

Существует несколько способов получить случайную синхронизацию, которая может объяснить это. Прежде всего, ваше приложение может бороться за ресурсы ЦП с другими приложениями, работающими на оборудовании, и приложение может быть заменено. Возможно, у вас больше потоков, чем у процессоров. Оба из них могут вызвать сброс грязной памяти в память ядра, когда потоки будут выгружены.

Другой вероятный сценарий состоит в том, что ваши потоки пересекают другие барьеры памяти, такие как вызов других synchronized методы или доступ к другим volatile поля. Например, мне интересно это утверждение, потому что некоторые потоки ввода / вывода имеют синхронизированные классы.

printData(generatePassword(counter), startTime);

Вы можете попытаться удалить печать данных, чтобы посмотреть, не изменится ли поведение вашего приложения.

Я говорю вам, что это прекрасно работает, и я проверил это с помощью sysouts. Это странно, и поэтому я задал этот вопрос:)

Прекрасный пример. System.out это PrintStream который является synchronized класс так зовет println() это приведет к тому, что ваш поток пересечет барьер памяти для чтения и записи, что обновит ваш static поле. Важно отметить, что любой барьер памяти влияет на всю кэшированную память. Преодоление любых барьеров чтения памяти вынуждает обновлять всю кэшированную память из центральной памяти. Преодоление любых барьеров памяти записи заставляет всю локальную грязную память быть записанной в центральную.

Проблема в том, когда вы удаляете System.out методы или когда ваше приложение перестает вызывать synchronized класс, а затем static переменная не обновлена ​​должным образом. Так что на это нельзя полагаться, но это случается.

Статические переменные: используются в контексте объекта, где обновление, выполненное одним объектом, будет отражаться во всех других объектах того же класса, но не в контексте потока, где обновление одного потока до статической переменной будет отражать изменения сразу для всех потоки (в их локальном кэше). Если два потока (предположим, t1 и t2) обращаются к одному и тому же объекту и обновляют переменную, которая объявлена ​​как статическая, это означает, что t1 и t2 могут сделать свою собственную локальную копию одного и того же объекта (включая статические переменные) в своем соответствующем кеше, поэтому Обновление t1 до статической переменной в его локальном кеше не будет отражаться в статической переменной для кеша t2.

Volatile переменная: если два потока (предположим, t1 и t2) обращаются к одному и тому же объекту и обновляют переменную, которая объявлена ​​как volatile, то это означает, что t1 и t2 могут создать свой собственный локальный кеш объекта, за исключением переменной, которая объявлена ​​как volatile, Таким образом, переменная volatile будет иметь только одну основную копию, которая будет обновляться различными потоками, а обновление, выполненное одним потоком для переменной volatile, будет немедленно отражено для другого потока.

Другие вопросы по тегам