Синхронизация потоков

Нарушение целостности данных

Вследствии неопределённости порядка доступа к ресурсам в многопоточных программах возникает целый ряд проблем. Если два или более потоков совместно используют общий ресурс, то неизвестно, в каком порядке они будут получать ресурс в своё распоряжение и работать с ним. Например, потоки t1 и t2 (см. ниже) пытаются изменить значение значение переменной-члена sharedResource. Порядок доступа к этой переменной неопределён - значение может быть сначала увеличено или, наоборот, уменьшено на единицу:

public class RaceCondition {
    int sharedResource = 0;

    public void startTwoThreads() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                sharedResource++;
            }
        }
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                sharedResource--;
            }
        }
        t2.start();
    }
}

Неопределённое состояние переменной sharedResource является следствием так называемого состояния гонки (race condition), возникающего, когда порядок выполнения ветвей кода в некотором фрагменте может отличаться от случая к случаю. Нельзя быть уверенным, что поток t1 всегда выполняется раньше, чем поток t2. Здесь источником проблем является не только неопределённый порядок выполнения потоков, но и тот факт, что операции увеличения и уменьшения на 1 представлены несколькими инструкциями байт-кода: чтение (read), изменение (modify) и запись (write). Переключения контекста могут происходить и в те моменты, когда инструкции байт-кода выполнены лишь частично, вследствии чего конечное значение переменной sharedResource становится зависимым от порядка выполнения: оно может быть равным 0, -1 и 1. Нуль получается, когда первый поток успевает выполнить запись значения до того, как его прочитает второй поток. Два остальных результата будут иметь место, если оба потока успеют прочитать начальное значение 0, а конечное значение зависит от того, какой поток выполнил запись последним.

Поскольку переключения контекста могут происходить, когда один поток выполняет часть кода, которая не должна прерываться, необходимо определить атомарные фрагменты (atomic regions) инструкций кода, всегда выполняемые полностью, без чередования с выполнением других потоков. Если поток выполняет атомарный фрагмент кода, другие потоки будут заблокированы (blocked), пока выполнение этого фрагмента не завершиться. В языке Java атомарный фрагмент называется взаимоисключающим (mutually exclusive), потому что доступен только для одного потока. Атомарный фрагмент можно определить разными способами, но наиболее обобщённым и часто применяемым механизмом синхронизации является ключен=вое слово syncronized:

syncronized (this) {
    sharedResource++;
}

Переписываем код:

public class RaceCondition {
    int sharedResource = 0;

    public void startTwoThreads() {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncronized (this) {
                    sharedResource++;
                }
            }
        }
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                syncronized (this) {
                    sharedResource--;
                }
            }
        }
        t2.start();
    }
}

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

Далее

Потоки

Поддержите проект, если он помог вам

Проект продвигается за счёт личных средств и времени авторского коллектива. Если вы нашли здесь то, что искали, то вы можете выразить свою благодарность финансово. Даже небольшой платёж помогает авторам в их труде, сохраняя их вовлечённость и высокую мотивацию чтобы строить открытый мир равных возможностей для всех неравнодушных людей вокруг.