Я пойду, ты не умеешь пользоваться синхронизированным

Java

Второй брат, оставь тебя в последней статьеЯ хожуПрошло две недели, а серия еще не собирается обновляться? Тревожно видеть это.

Выше сообщение, отправленное читателем Джейсоном. Не знаю, не читал ли я его. Это действительно шокирует, увидев это. Последний раз, когда я заходил туда, было обновление 3 апреля. Прошло больше месяца. теперь не две недели. Может быть, я и сам пишу каждый день, но я не думаю, что это было так давно, пора завести новое «Я иду».

На этот раз проверки кода не было, и мой коллега Сяо Ван прямо спросил меня: «Брат Цин, не могли бы вы подробно рассказать мне, как использовать ключевое слово synchronized?» Он спросил очень скромно, но я не мог не закричать: « Черт, Сяо Ван, ты не знаешь, как использовать синхронизацию, как я вообще взял у тебя интервью!»

(Мой псевдоним — Тихий Ван Эр, а читателей зовут Эр Гэ, но не в компании. Мои коллеги зовут меня Цин Гэ. Если вы хотите узнать мое настоящее имя, вы можете выполнить поиск по запросу «Продвинутый путь к веб-полному пакету». Разработка")

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

01. Зачем нужна защита?

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

public class SynchronizedMethod {
    private int sum;

    public int getSum() {
        return sum;
    }

    public void setSum(int sum) {
        this.sum = sum;
    }

    public void calculate() {
        setSum(getSum() + 1);
    }
}

SynchronizedMethod — это очень простой класс с закрытой переменной-членом sum, соответствующим получателем/установщиком и методом, который добавляет 1 к сумме.calculate()метод.

Тогда давайтеcalculate()Метод написания простого тестового примера.

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

Первый шаг — навести указатель мыши на имя класса, после чего появится всплывающее окно.

Второй шаг, нажмите кнопку «Дополнительные действия», появится следующее окно подсказки.

Третий шаг, выберите «Создать тест», появится диалоговое окно для создания тестового примера.

Выберите последнюю версию JUnit5, если в проекте раньше не было зависимостей JUnit5, IDEA напомнит вам, нажмите «Исправить», IDEA автоматически добавит его для вас, очень умно. В диалоговом окне отметьте способ создания тестового примера —calculate().

После нажатия кнопки «ОК» IDEA создаст тестовый класс с именем SynchronizedMethodTest в каталоге того же уровня, что и src:

class SynchronizedMethodTest {
    @Test
    void calculate() {
    }
}

calculate()метод будет иметь@Test, что указывает на то, что это тестовый метод. Добавьте конкретный код следующим образом:

ExecutorService service = Executors.newFixedThreadPool(3);
SynchronizedMethod summation = new SynchronizedMethod();

IntStream.range(0, 1000)
        .forEach(count -> service.submit(summation::calculate));
service.awaitTermination(1000, TimeUnit.MILLISECONDS);

assertEquals(1000, summation.getSum());

1)Executors.newFixedThreadPool()метод для создания службы пула потоков ExecutorService указанного размера.

2) пройтиIntStream.range(0, 1000).forEach()выполнитьcalculate()метод 1000 раз.

3) пройтиassertEquals()метод судить.

Запустите тестовый пример, что будет в результате?

К сожалению, это не удалось. Ожидаемое значение — 1000, но фактическое значение — 976. Это связано с тем, что в многопоточной среде изменяемые общие данные не защищены.

02. Использование синхронизированного

Скажем так, когда новички сталкиваются с проблемами многопоточности, если ключевое слово synchronized используется правильно, проблема может быть легко решена. Я помню, когда я впервые вернулся в Лоян, интервьюер спросил меня, как проект решил проблему параллелизма? Я просто сказал использовать ключевое слово synchronized, а о некоторых других механизмах блокировки я тогда не знал.

Что ж, интервьюер, похоже, тоже не знает, потому что в небольших компаниях параллелизм ограничен, и производительность не нужно рассматривать слишком глубоко (читатели крупных компаний могут смеяться). Далее, пойдемте со мной и взгляните на три наиболее распространенных варианта использования synchronized.

1) Используйте его непосредственно в методе, например:

public synchronized void synchronizedCalculate() {
    setSum(getSum() + 1);
}

Измените тестовый пример:

@Test
void synchronizedCalculate() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethod summation = new SynchronizedMethod();

    IntStream.range(0, 1000)
            .forEach(count -> service.submit(summation::synchronizedCalculate));
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);

    assertEquals(1000, summation.getSum());
}

В это время снова запустите тестовый пример, и он пройдет. Поскольку ключевое слово synchronized блокирует объект SynchronizedMethod, только один поток может одновременно изменять сумму. Как будто есть комната. Поток может добавить к сумме только 1, когда он входит в комнату, а синхронизация эквивалентна добавлению замка к двери. После входа одного потока дверь запирается. После изменения суммы следующий поток входит снова Другие потоки ждали сразу за дверью.

2) Используется в статических методах, например:

public class SynchronizedStaticMethod {
    public static int sum;

    public synchronized static void synchronizedCalculate() {
        sum = sum + 1;
    }
}

sum является статической переменной.Если вы хотите изменить статическую переменную, вам нужно сделать метод статическим.

Чтобы создать новый тестовый пример:

class SynchronizedStaticMethodTest {
    @Test
    void synchronizedCalculate() throws InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(3);

        IntStream.range(0, 1000)
                .forEach(count -> service.submit(SynchronizedStaticMethod::synchronizedCalculate));
        service.awaitTermination(1000, TimeUnit.MILLISECONDS);

        assertEquals(1000, SynchronizedStaticMethod.sum);
    }
}

Когда вы добавляете синхронизированный к статическому методу, вам не нужно создавать экземпляр объекта, вы можете ссылаться на метод и использовать переменную напрямую, используя имя класса. Тест-кейс тоже проходной.

Разница между синхронизированным статическим и синхронизированным заключается в том, что первый блокирует класс, и только один поток может получить доступ к классу одновременно; второй блокирует объект, и только один поток может получить доступ к методу одновременно.

3) Используется в блоках методов, например:

public void synchronisedThis() {
    synchronized (this) {
        setSum(getSum() + 1);
    }
}

В это время это передается синхронизированному блоку кода.Когда этот блок кода выполняется в потоке, поток получит блокировку этого объекта, так что другие потоки не могут получить доступ к блоку кода в то же время. Если метод статический, мы будем передавать имя класса вместо ссылки на объект, пример будет выглядеть так:

public static void synchronisedThis() {
    synchronized (SynchronizedStaticMethod.class) {
        sum = sum + 1;
    }
}

Создайте новый тестовый случай:

@Test
void synchronisedThis() throws InterruptedException {
    ExecutorService service = Executors.newFixedThreadPool(3);
    SynchronizedMethod summation = new SynchronizedMethod();

    IntStream.range(0, 1000)
            .forEach(count -> service.submit(summation::synchronisedThis));
    service.awaitTermination(1000, TimeUnit.MILLISECONDS);

    assertEquals(1000, summation.getSum());
}

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

Что ж, мои дорогие читатели и друзья, вышеизложенное составляет все содержание этой статьи.Вы должны освоить три варианта использования synchronized, верно? Если вы считаете, что статья полезна, введите в поиск «Тихий король 2"Читал в первый раз.

эта статьяGitHubОн был включен, и есть полные тестовые площадки для интервью на крупных заводах Добро пожаловать в Star.

Я Silent King Er, интересный программист.Обратите внимание на повышение эффективности обучения. Наконец, пожалуйста, безжалостно лайкните, добавьте в закладки и оставьте сообщение, спасибо.