Друкарня від WE.UA

Java. Чому не можна синхронізуватись на об'єктах-враперах?

Всі знають, що якщо нам потрібно зробити якусь неатомарну операцію, треба засинхронізуватись.
Давайте так і зробимо. Знизу дефолтний приклад інкрементації змінної з декілької потоків.

public class Test {
    public static int counter = 0;
    public static Integer LOCK = Integer.MIN_VALUE;

    public static void increment() {
        // synchronize using lock of wrapper class
        synchronized (LOCK) {
            counter++;
            LOCK++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // Runnable which processing increment of LOCK variable
        Runnable runnable = () -> {
            for (int i = 0; i < 1_000_000; i++) {
                increment();
            }
        };
        // starting threads
        Thread t1 = new Thread(runnable);
        Thread t2 = new Thread(runnable);
        Thread t3 = new Thread(runnable);
        Thread t4 = new Thread(runnable);
        startAndJoin(t1, t2, t3, t4);
        System.out.println(counter);
    }

    private static void startAndJoin(Thread... threads) throws InterruptedException {
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
    }
}

Локом буде виступає об’єкт класу Integer.
Результат виконання main методу “2075471”.
А мало бути 4кк, оскільки кожен із 4 потоків накручує по 1кк:

Чому так відбувається?

Перший потік займає монітор, виконує свою логіку, в цей час інший потік який також хотів прокрутити свою логіку натрапив на монітор, який його не пустив і монітор поклав цей потік собі в чергу(*1).
І повідомив про це операційній системі, яка тепер, при виході першого потоку(вона буде за цим слідкувати) має витягнути інший потік(який “чекав в черзі“) і покласти його в іншу чергу яка називається ready queue, яку ж потім запустить на виконання сама ОС згідно свого планувальника.
Але, від об’єкта нічого не залишилось(*2), бо під час інкрементування першим потоком він зник і на його місце прийшов новий об’єкт(а з ним, його монітор, зі своєю чергою очікуючих потоків).
Тому ті інші потоки/потік, які хотіли виконувати свої операції — вони так їх і не виконають, тому і відбуваються “прослизання“ в інкрементації лічильника.

*1 — кожен монітор володіє зовнішньою чергою, зовнішня вона тому, що за нею слідкує сама ОС.
Це waiting-list потоків, які хочуть зайняти монітор і виконати відповідні операції.

*2 — Числові врапери є імутабельними. Вони всередині реалізовані за патерном "Одинак" і будь-яка спроба підкрутити йому значення/змінити стан об’єкта, призводить до того, що цей об’єкт зникне.


https://puredanger.github.io/tech.puredanger.com/2009/01/28/java-concurrency-bugs-synchronize-object/

Статті про вітчизняний бізнес та цікавих людей:

Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Oleksandr Klymenko
Oleksandr Klymenko@overpathz

Java Software Engineer

40Довгочити
9.5KПерегляди
104Підписники
На Друкарні з 19 квітня 2023

Більше від автора

  • Java Memory Model ↔ PostgreSQL MVCC: один і той самий фундамент конкурентності

    Java Memory Model та PostgreSQL MVCC/Isolation Levels — це дві відповіді на одне й те саме фундаментальне питання: за яких умов зміна, зроблена одним учасником (потоком / транзакцією), стає видимою для іншого?

    Теми цього довгочиту:

    Java
  • Як виконується SQL-запит

    Сьогодні розберемо, що відбувається з вашим SELECT * FROM users WHERE age > 21 перед тим, як БД поверне вам дані

    Теми цього довгочиту:

    Computer Science

Це також може зацікавити:

  • Типи даних в Rust (part 1)

    Одним із ключових аспектів розробки програм на Rust є типи даних. У цій статті ми розглянемо основні ТД, як використовуються, особливості.

    Теми цього довгочиту:

    Програмування
  • Palo-Alto-Networks Cybersecurity-Apprentice Exam Study Guide

    Prepare for the Palo-Alto-Networks Cybersecurity-Apprentice exam with this complete study guide. Explore practice questions, study materials, exam preparation tips, and proven strategies to build cybersecurity skills and boost your confidence for certification success.

    Теми цього довгочиту:

    It
  • Еволюція ллмок 2024

    Коротше ото ж всі побігли рік тому терміново робити свої ллмки і бабки збирати під свої єбейші стартапи.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Llm

Коментарі (0)

Підтримайте автора першим.
Напишіть коментар!

Це також може зацікавити:

  • Типи даних в Rust (part 1)

    Одним із ключових аспектів розробки програм на Rust є типи даних. У цій статті ми розглянемо основні ТД, як використовуються, особливості.

    Теми цього довгочиту:

    Програмування
  • Palo-Alto-Networks Cybersecurity-Apprentice Exam Study Guide

    Prepare for the Palo-Alto-Networks Cybersecurity-Apprentice exam with this complete study guide. Explore practice questions, study materials, exam preparation tips, and proven strategies to build cybersecurity skills and boost your confidence for certification success.

    Теми цього довгочиту:

    It
  • Еволюція ллмок 2024

    Коротше ото ж всі побігли рік тому терміново робити свої ллмки і бабки збирати під свої єбейші стартапи.

    Публікація містить описи/фото насилля, еротики або іншого чутливого контенту.

    Теми цього довгочиту:

    Llm