Як синхронізувати або заблокувати змінні в Java?


77

Дозвольте використати цей невеликий і простий зразок:

class Sample {
    private String msg = null;

    public void newmsg(String x){
        msg = x;
    }

    public String getmsg(){
        String temp = msg;
        msg = null;
        return temp;
    }
}

Припустимо, функція newmsg()викликається іншими потоками, до яких я не маю доступу.

Я хочу використовувати метод synchonize, щоб гарантувати, що рядок msgвикористовується лише однією функцією за раз. Іншими словами, функція newmsg()не може працювати одночасно з getmsg().


4
Ви запитуєте, як використовувати "синхронізоване" ключове слово в Java? Пошук простого Google повертається з великою кількістю корисних хітів , включаючи це один download.oracle.com/javase/tutorial/essential/concurrency / ...
Кал

4
До речі, було б набагато краще назвати наш метод getmsg () десь на зразок popmsg () або consumemsg (), оскільки він модифікує клас
naumcho

Відповіді:


171

Це досить просто:

class Sample {
    private String message = null;
    private final Object lock = new Object();

    public void newMessage(String x) {
        synchronized (lock) {
            message = x;
        }
    }

    public String getMessage() {
        synchronized (lock) {
            String temp = message;
            message = null;
            return temp;
        }
    }
}

Зверніть увагу, що я не робив самі методи синхронізованими, ані синхронізувати this. Я твердо впевнений, що корисно отримувати блокування лише на об'єктах, до яких має доступ лише ваш код, якщо ви не навмисно викриваєте блокування. Це набагато легше запевнити себе, що ніщо інше не збирається отримувати замки в іншому порядку, порівняному з вашим кодом тощо.


8
у чому різниця між встановленням функції синхронізації або створенням цього блокування?
Зима

3
@Giacomo: Мені подобається, щоб мій код був читабельним і розробленим таким чином, щоб я міг про це міркувати. Це подвійно важливо, коли бере участь багатопоточність. Не буде перебільшенням сказати, що саме такий код я майже завжди використовую. Щодо марнотратства ... як часто ви очікуєте, що ці відходи будуть значними?
Джон Скіт,

3
@Giacomo: Справа в тому, що очевидно, що ти замикаєшся на чомусь, що ніхто інший не може. Це здається досить простим, щоб міркувати про те, що ви прочитали код і готово. А тепер розглянемо ваш приклад: звідки ви знаєте, що може синхронізуватися на вашому об’єкті? Як ви можете зрозуміти поведінку блокування? Ви повинні прочитати весь задіяний код. Тому я б сказав, що це менш читабельно, якщо вас насправді турбує правильна поведінка. Чесно кажучи, я вважаю помилкою дозволити блокувати всі об'єкти в Java. (Помилка скопійована .NET, на жаль.)
Джон Скіт,

3
Синхронізований @Winter метод блокує "це", отже сам об'єкт Sample. Але синхронізований блок на "блокуванні об'єкта" блокуватиме "блокування", а не зразок
Джемшит Іскендеров

2
@PhilipRego: По-перше, тому що тоді значення, яке блокується, зміниться, що робить речі небезпечними різними способами. По-друге, тому що це може легко означати ненавмисне спільне використання замків між кількома об'єктами, що призводить до різного роду інших проблем. По-третє: розділення проблем. Я дуже захоплююсь полем, яке має одну мету.
Джон Скіт,

53

Для цієї функції вам краще взагалі не використовувати замок. Спробуйте AtomicReference.

public class Sample {
    private final AtomicReference<String> msg = new AtomicReference<String>();

    public void setMsg(String x) {
        msg.set(x);
    }

    public String getMsg() {
        return msg.getAndSet(null);
    }
}

Замки не потрібні, а код простіший IMHO. У будь-якому випадку він використовує стандартну конструкцію, яка робить те, що ви хочете.


9
FYI є класи java.util.concurrent.atomicдля примітивних типів, таких як AtomicBooleanіAtomicInteger
Х'ю Джеффнер

10

З Java 1.5 завжди гарна ідея розглянути пакет java.util.concurrent. Вони є сучасним механізмом блокування в Java зараз. Механізм синхронізації є більш важким, ніж класи java.util.concurrent.

Приклад може виглядати приблизно так:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Sample {

    private final Lock lock = new ReentrantLock();

    private String message = null;

    public void newmsg(String msg) {
        lock.lock();
        try {
            message = msg;
        } finally {
            lock.unlock();
        }
    }

    public String getmsg() {
        lock.lock();
        try {
            String temp = message;
            message = null;
            return temp;
        } finally {
            lock.unlock();
        }
    }
}

1
згідно з документами , блокування слід зробити перед блоком спроби.
Aaron Roller

3

У цьому простому прикладі ви можете просто поставити synchronizedяк модифікатор після publicобох підписів методів.

Більш складні сценарії вимагають інших речей.


3

Використовуйте synchronizedключове слово.

class sample {
    private String msg=null;

    public synchronized void newmsg(String x){
        msg=x;
    }

    public synchronized string getmsg(){
        String temp=msg;
        msg=null;
        return msg;
    }
}

Використання synchronizedключового слова для методів вимагатиме потоків для отримання блокування в екземплярі sample. Таким чином, якщо входить будь-який потік newmsg(), жоден інший потік не зможе отримати блокування на екземплярі sample, навіть якщо він намагався викликати getmsg().

З іншого боку, використання synchronizedметодів може стати вузьким місцем, якщо ваші методи виконують тривалі операції - всім потокам, навіть якщо вони хочуть викликати інші методи в цьому об’єкті, які можуть бути перемежовані, все одно доведеться чекати.

IMO, у вашому простому прикладі нормально використовувати синхронізовані методи, оскільки у вас насправді є два методи, які не слід перемежовувати. Однак за різних обставин може мати сенс мати об’єкт блокування для синхронізації, як показано у відповіді Джо Скіта.


2

Якщо в іншому випадку ви синхронізуєте колекцію, а не рядок, можливо, ви перебираєте колекцію і турбуєтесь про її мутацію, Java 5 пропонує:

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.