java - описание - thread sync что это




Что означает «синхронизированный»? (10)

У меня есть некоторые вопросы относительно использования и значимости synchronized ключевого слова.

  • Каково значение synchronized ключевого слова?
  • Когда следует synchronized методы?
  • Что значит программно и логически?

Что такое синхронизированное ключевое слово?

Потоки передаются, главным образом, путем совместного доступа к полям и полям ссылок на объекты. Эта форма коммуникации чрезвычайно эффективна, но делает возможными два вида ошибок: помехи потоков и ошибки согласованности памяти . Инструмент, необходимый для предотвращения этих ошибок, - это синхронизация.

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

Когда синхронизируются методы?

Методы синхронизируются при добавлении synchronized к определению метода или декларации. Вы также можете синхронизировать определенный блок кода с помощью метода.

Что означает про грамматически и логически?

Это означает, что только один поток может получить доступ к критическому разделу путем приобретения блокировки. Если этот поток не освободит эту блокировку, все остальные нити (ы) придется ждать, чтобы получить блокировку. У них нет доступа для входа в критический раздел без блокировки.

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

Подробнее о page документации java

Внутренние замки и синхронизация:

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

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

Говорят, что поток имеет собственный замок между временем, когда он приобрел замок, и освободил замок. Пока поток владеет встроенной блокировкой, ни один другой поток не может получить одну и ту же блокировку. Другой поток будет блокироваться, когда он попытается получить блокировку.

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

Синхронизация методов синхронизации имеет два effects :

Во-первых, невозможно, чтобы две вызовы синхронизированных методов на одном объекте чередовали.

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

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

Это гарантирует, что изменения состояния объекта будут видны для всех потоков.

Ищите другие альтернативы синхронизации в:

Избегайте синхронизации (это) в Java?


обзор

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

Синхронизированное ключевое слово используется для определения блока кода, в котором несколько потоков могут безопасно обращаться к одной и той же переменной.

Глубже

Синтаксически synchronized ключевое слово принимает Object как его параметр (называемый объектом блокировки ), за которым следует { block of code } .

  • Когда выполнение встречает это ключевое слово, текущий поток пытается «блокировать / приобретать / владеть» (выбирать) объект блокировки и выполнять соответствующий блок кода после того, как блокировка была приобретена.

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

  • Только один поток за раз может удерживать блокировку, в течение которого все остальные потоки, пытающиеся получить один и тот же объект блокировки, будут ждать (приостановить их выполнение). Блокировка будет отключена, когда выполнение завершит синхронизированный блок кода.

Синхронизированные методы:

Добавление synchronized ключевого слова в определение метода равно всему телу метода, который обернут в синхронизированный блок кода, причем объект блокировки является this (например, методы) и ClassInQuestion.getClass() (для методов класса) .

- Метод экземпляра - это метод, который не имеет static ключевого слова.
Метод класса - это метод, который имеет static ключевое слово.

технический

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

Недостаточно выполнить операцию записи в потоке до (настенное время) другой поток читает его, потому что аппаратное обеспечение может кэшировать значение переменной, а поток чтения будет видеть кешированное значение вместо того, что было написано для Это.

Заключение

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

источники

http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Спецификация Java® Language, 2015-02-13


Какие другие ответы отсутствуют, это один важный аспект: барьеры памяти . Синхронизация потоков в основном состоит из двух частей: сериализации и видимости. Я советую всем google «барьер памяти jvm», поскольку это нетривиальная и чрезвычайно важная тема (если вы изменяете общие данные, к которым обращаются несколько потоков). Сделав это, я советую посмотреть классы пакета java.util.concurrent, которые помогают избежать использования явной синхронизации, что, в свою очередь, помогает поддерживать простые и эффективные программы, возможно, даже предотвращая взаимоблокировки.

Одним из таких примеров является ConcurrentLinkedDeque . Вместе с командным шаблоном он позволяет создавать высокоэффективные рабочие потоки, набирая команды в параллельную очередь - не требуется явная синхронизация, нет взаимоблокировок, нет явного сна (), необходимо просто опросить очередь, вызвав take ().

Короче говоря, «синхронизация памяти» происходит неявно при запуске потока, поток заканчивается, вы читаете переменную volatile, вы разблокируете монитор (оставляете синхронизированный блок / функцию) и т. Д. Эта «синхронизация» влияет (в некотором смысле «флеш» ») все записи делаются до этого конкретного действия. В случае вышеупомянутого ConcurrentLinkedDeque документация «говорит»:

Эффекты согласованности памяти. Как и в случае с другими параллельными коллекциями, действия в потоке перед размещением объекта в ConcurrentLinkedDeque happen-before действий после доступа или удаления этого элемента из ConcurrentLinkedDeque в другом потоке.

Это неявное поведение является несколько пагубным аспектом, потому что большинство программистов Java, не имеющих большого опыта, просто возьмут много, как указано из-за этого. И затем внезапно наткнуться на этот поток после того, как Java не делает то, что «предполагается» делать в производстве, где есть другая рабочая нагрузка, и довольно сложно проверить проблемы параллелизма.


Насколько я понимаю, синхронизация в основном означает, что компилятор пишет monitor.enter и monitor.exit вокруг вашего метода. Как таковой он может быть потокобезопасным в зависимости от того, как он используется (я имею в виду, что вы можете написать объект с синхронизированными методами, который не является потокобезопасным в зависимости от того, что делает ваш класс).


Подумайте об этом как о турникете, как вы можете найти на футбольной площадке. Есть паралисты людей, желающих войти, но на турникете они «синхронизированы». Только один человек за один раз может пройти. Все, кто хочет пройти, будут делать, но им, возможно, придется подождать, пока они не смогут пройти.


Синхронизированное просто означает, что несколько потоков, если они связаны с одним объектом, могут предотвратить загрязнение чтения и записи, если синхронизируемый блок используется для конкретного объекта. Чтобы дать вам больше ясности, давайте возьмем пример:

class MyRunnable implements Runnable {
    int var = 10;
    @Override
    public void run() {
        call();
    }

    public void call() {
        synchronized (this) {
            for (int i = 0; i < 4; i++) {
                var++;
                System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
            }
        }
    }
}

public class MutlipleThreadsRunnable {
    public static void main(String[] args) {
        MyRunnable runnable1 = new MyRunnable();
        MyRunnable runnable2 = new MyRunnable();
        Thread t1 = new Thread(runnable1);
        t1.setName("Thread -1");
        Thread t2 = new Thread(runnable2);
        t2.setName("Thread -2");
        Thread t3 = new Thread(runnable1);
        t3.setName("Thread -3");
        t1.start();
        t2.start();
        t3.start();
    }
}

Мы создали два объекта класса MyRunnable, а runnable1 - совместно с потоком 1, а поток 3 и runnable2 - только с потоком 2. Теперь, когда t1 и t3 запускаются без использования синхронизации, вывод PFB, который предполагает, что оба потока 1 и 3 одновременно влияют на значение var, где для потока 2 var имеет свою собственную память.

Without Synchronized keyword

    Current Thread Thread -1 var value 11
    Current Thread Thread -2 var value 11
    Current Thread Thread -2 var value 12
    Current Thread Thread -2 var value 13
    Current Thread Thread -2 var value 14
    Current Thread Thread -1 var value 12
    Current Thread Thread -3 var value 13
    Current Thread Thread -3 var value 15
    Current Thread Thread -1 var value 14
    Current Thread Thread -1 var value 17
    Current Thread Thread -3 var value 16
    Current Thread Thread -3 var value 18

Используя Synchronzied, поток 3 ожидает завершения потока 1 во всех сценариях. Имеются две блокировки, одна на runnable1, совместно используемая нитью 1 и потоком 3, а другая на runnable2, совместно используемая только потоком 2.

Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18

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

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

Object.wait()

Synchronized normal method эквивалентный Synchronized statement (используйте это)

class A {
    public synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(this) {
             // all function code
        }
    } 
}

Synchronized static method эквивалентный Synchronized statement (класс использования)

class A {
    public static synchronized void methodA() {
        // all function code
    }

    equivalent to

    public void methodA() {
        synchronized(A.class) {
             // all function code
        }
    } 
}

Синхронизированный оператор (с использованием переменной)

class A {
    private Object lock1 = new Object();

    public void methodA() {
        synchronized(lock1 ) {
             // all function code
        }
    } 
}

Для synchronized мы имеем как Synchronized Methods и Synchronized Statements . Однако Synchronized Methods похожи на Synchronized Statements поэтому нам просто нужно понять Synchronized Statements .

=> В принципе, у нас будет

synchronized(object or class) { // object/class use to provides the intrinsic lock
   // code 
}

Вот 2 думают, что помогают понять synchronized

  • Каждый объект / класс имеет связанный с ним intrinsic lock .
  • Когда поток вызывает synchronized statement , он автоматически получает intrinsic lock для объекта synchronized statement's и освобождает его при возврате метода. Пока поток владеет intrinsic lock , NO другой поток может получить SAME lock => thread safe.

=> Когда thread A вызывает synchronized(this){// code 1} => весь блок-код (внутри класса), где synchronized(this) и весь synchronized normal method (внутри класса) заблокирован, потому что SAME заблокирован. Он будет выполняться после разблокировки thread A («// code 1» завершен).

Это поведение похоже на synchronized(a variable){// code 1} или synchronized(class) .

SAME LOCK => блокировка (не зависит от того, какой метод? Или какие заявления?)

Использовать синхронизированный метод или синхронизированные операторы?

Я предпочитаю synchronized statements потому что он более расширяем. Например, в будущем вам потребуется только синхронизация части метода. Например, у вас есть 2 синхронизированный метод, и он не имеет никакого отношения к друг другу, однако, когда поток запускает метод, он блокирует другой метод (он может предотвратить использование synchronized(a variable) ).

Однако применять синхронизированный метод просто, и код выглядит просто. Для некоторого класса существует только один синхронизированный метод или все синхронизированные методы в классе, относящиеся друг к другу => мы можем использовать synchronized method чтобы сделать код короче и легко понятным

Заметка

(он не имеет большого отношения к synchronized , он отличается от объекта и класса или нестационарный и статический).

  • Когда вы используете synchronized или нормальный метод или synchronized(this) или synchronized(non-static variable) он будет синхронизироваться с базой на каждом экземпляре объекта.
  • Когда вы используете synchronized или статический метод или synchronized(class) или synchronized(static variable) он будет синхронизирован базой по классу

Ссылка

effects page

Надеюсь, что это поможет


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

Это часто называют созданием класса потокобезопасным, но я бы сказал, что это эвфемизм. Хотя верно, что синхронизация защищает внутреннее состояние Vector от повреждения, это обычно не помогает пользователю Vector.

Учти это:

 if (vector.isEmpty()){
     vector.add(data);
 }

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

Таким образом, вы также должны синхронизировать свой код приложения.

Поскольку синхронизация на уровне метода является a) дорогой, когда она вам не нужна, и b) недостаточно, когда вам нужна синхронизация, теперь есть несинхронизированные замены (ArrayList в случае Vector).

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


synchronized ключевое слово предотвращает параллельный доступ к блоку кода или объекта несколькими потоками. По умолчанию Hashtable synchronized , поэтому только один поток может одновременно обращаться к таблице.

При использовании non-synchronized конструкций, таких как HashMap , вы должны создавать функции безопасности потоков в своем коде, чтобы предотвратить ошибки согласованности памяти.





synchronized