1. Когда возникнут проблемы с безопасностью резьбы?
В одном потоке не будет проблем с безопасностью потока, но в многопоточном программировании можно получить доступ к одному и тому же ресурсу одновременно. Этот ресурс может быть различными типами ресурсов: переменная, объект, файл, таблица базы данных и т. Д. Когда несколько потоков получают доступ к одному и тому же ресурсу одновременно, возникнет проблема:
Поскольку процесс, выполняемый каждым потоком, является неконтролируемым, вполне вероятно, что конечный результат будет противоречить фактическому желанию или напрямую приведет к ошибкам программы.
Давайте приведем простой пример:
Теперь есть два потока, которые считывают данные из сети отдельно, а затем вставляют их в таблицу базы данных, что требует, чтобы дубликаты не могли быть вставлены.
Тогда должно быть две операции в процессе вставки данных:
1) проверить, существуют ли данные в базе данных;
2) Если он существует, он не будет вставлен; Если его не существует, он будет вставлен в базу данных.
Если эти два потока представлены потоком-1 и потоком 2 соответственно, а в какой-то момент поток-1 и Thread-2 оба считывают данные x, это может произойти: Это может произойти:
Thread-1 проверяет, существует ли данные x в базе данных, и Thread-2 также проверяет, существует ли данные X в базе данных.
В результате результат проверки двух потоков заключается в том, что данные X не существует в базе данных, поэтому оба потока вставляют данные X в таблицу базы данных соответственно.
Это проблема безопасности потока, то есть, когда несколько потоков получают доступ к ресурсу одновременно, результат запуска программы не является результатом, который вы хотите увидеть.
Здесь этот ресурс называется: критический ресурс (также известный как общий ресурс).
То есть, когда несколько потоков получают доступ к критическим ресурсам (один объект, атрибуты в объекте, файл, базу данных и т. Д.), Могут возникнуть проблемы безопасности потоков.
Однако, когда несколько потоков выполняют метод, локальные переменные внутри метода не являются критическими ресурсами, поскольку метод выполняется в стеке, в то время как стек Java является потоковым, поэтому не будет проблем с безопасностью потоков.
2. Как решить проблемы безопасности потока?
Итак, как решить проблемы безопасности потока?
По сути, при решении проблем безопасности потоков все режимы параллелизма принимают решение «сериализованное доступ к критическим ресурсам», то есть в то же время только один поток может получить доступ к критическим ресурсам, также известный как синхронный взаимоисключающий доступ.
Вообще говоря, блокировка добавляется перед кодом, который обращается к критическому ресурсу. После доступа к критическому ресурсу блокировка выпускается, а другие потоки продолжают доступ.
В Java представлены два способа реализации синхронного доступа к Mutex: синхронизированный и блокировка.
В этой статье в основном рассказывается об использовании синхронизации, и использование блокировки описано в следующем посте в блоге.
Три. Синхронизированный метод синхронизации или блок синхронизации
Прежде чем понимать использование синхронизированных ключевых слов, давайте сначала рассмотрим концепцию: Mutex Locks, как следует из названия: замки, которые могут достичь цели Mutex Access.
Чтобы привести простой пример: если критический ресурс добавляется в мутекс, когда один поток обращается к критическому ресурсу, другие потоки могут только ждать.
В Java каждый объект имеет отметку блокировки (монитор), также известный как монитор. Когда несколько потоков получают доступ к объекту одновременно, поток может получить доступ к нему, только если он получает блокировку объекта.
В Java синхронизированное ключевое слово может использоваться для обозначения метода или блока кода. Когда поток вызывает синхронизированный метод объекта или обращается к синхронизированному кодовому блоку, поток получает блокировку объекта. Другие потоки не могут получить доступ к методу в настоящее время. Только когда выполняется метод или будет выполнен кодовый блок, выпустит поток блокировку объекта, а другие потоки могут выполнить метод или блок кода.
Ниже приведены несколько простых примеров, чтобы проиллюстрировать использование синхронизированных ключевых слов:
1. Синхронизированный метод
В следующем коде два потока вызывают объект InsertData, чтобы вставить данные:
открытый тест класса {public static void main (string [] args) {final insertData insertData = new insertData (); new Thread () {public void run () {insertData.insert (thread.currentThread ()); }; }.начинать(); new Thread () {public void run () {insertData.insert (thread.currentThread ()); }; }.начинать(); }} класс InsertData {private ArrayList <Integer> arraylist = new ArrayList <Integer> (); public void insert (поток потока) {for (int i = 0; i <5; i ++) {System.out.println (thread.getName ()+"Вставить данные"+i); arraylist.add (i); }}} В настоящее время выходным результатом программы является:
Это показывает, что два потока выполняют метод вставки одновременно.
Если синхронизированный ключевой слов добавляется перед методом вставки, результат выполнения:
Class InsertData {private ArrayList <Integer> arraylist = new ArrayList <Integer> (); public Synchronized void вставка (потока потока) {for (int i = 0; i <5; i ++) {System.out.println (thread.getName ()+"вставить данные"+i); arraylist.add (i); }}}Из приведенного выше вывода данные, вставленные в потоку-1, выполняются только после вставки потока-0. Это показывает, что методы вставки Thread-0 и Thread-1 последовательно.
Это синхронизированный метод.
Тем не менее, есть несколько моментов, чтобы отметить:
1) Когда поток обращается к синхронизированному методу объекта, другие потоки не могут получить доступ к другим синхронизированным методам объекта. Эта причина очень проста, потому что у объекта есть только один замок. Когда поток получает блокировку объекта, другие потоки не могут получить блокировку объекта, поэтому они не могут получить доступ к другим синхронизированным методам объекта.
2) Когда поток обращается к синхронизированному методу объекта, другие потоки могут получить доступ к несинхронизированному методу объекта. Причина этого очень проста. Доступ к несинхронизированным методам не требует блокировки объекта. Если метод не изменен с помощью синхронизированного ключевого слова, это означает, что он не будет использовать критические ресурсы, то другие потоки могут получить доступ к этому методу.
3) Если поток A необходимо для доступа к синхронизированному методу FUN1 Object Object1, а другой поток B должен получить доступ к синхронизированному методу FUN1 Object Object2, даже если Object1 и Object2 - один и тот же тип), не будет проблем с безопасностью потока, поскольку они получают доступ к различным объектам, поэтому не существует проблемы взаимного исключения.
2. Синхронизированный кодовый блок
Синхронизированные кодовые блоки аналогичны следующей форме:
синхронизированный (Syncobject) {
}
Когда этот блок кода выполняется в потоке, поток получает блокировку синообъекта объекта, что делает невозможным одновременно для других потоков к блоку кода.
Synobject может быть таким, представляющий блокировку, которая получает текущий объект, или он может быть атрибутом в классе, представляющий блокировку, которая приобретает атрибут.
Например, приведенный выше метод вставки может быть изменен на следующие две формы:
Class InsertData {private ArrayList <Integer> arraylist = new ArrayList <Integer> (); public void insert (поток потока) {синхронизированный (this) {for (int i = 0; i <100; i ++) {System.out.println (thread.getName ()+"вставить данные"+i); arraylist.add (i); }}}} класс InsertData {private ArrayList <Integer> arraylist = new ArrayList <Integer> (); частный объект объект = new Object (); public void insert (поток потока) {синхронизированный (Object) {for (int i = 0; i <100; i ++) {System.out.println (thread.getName ()+"вставить данные"+i); arraylist.add (i); }}}}Как видно из вышеизложенного, синхронизированные кодовые блоки гораздо более гибки, чем синхронизированные методы. Поскольку, возможно, только часть кода в методе должна быть синхронизирована, если в настоящее время весь метод синхронизирован, это повлияет на эффективность выполнения программы. Этой проблемы можно избежать, используя синхронизированные кодовые блоки. Синхронизированные кодовые блоки могут только синхронизировать, где требуется синхронизация.
Кроме того, каждый класс также имеет блокировку, которую можно использовать для управления параллельным доступом к статическим элементам данных.
И если поток выполняет нестатический синхронизированный метод объекта, а другой поток должен выполнить статический синхронизированный метод класса, к которому принадлежит объект, в это время не будет взаимного исключения, поскольку доступ к статическому синхронизированному методу занимает блокировку класса, хотя и доступа к нестатическому синхронизированному методу, поэтому нет исключения мутоулищего.
Вы поймете, посмотрите на следующий код:
открытый тест класса {public static void main (string [] args) {final insertData insertData = new insertData (); new Thread () {@Override public void run () {insertData.insert (); } }.начинать(); new Thread () {@Override public void run () {insertData.insert1 (); } }.начинать(); }} класс insertData {public synchronized void insert () {System.out.println ("execute insert"); try {thread.sleep (5000); } catch (прерванное искусство e) {e.printstacktrace (); } System.out.println ("execute insert1"); } public static void insert1 () {System.out.println ("execute insert1"); System.out.println ("execute insert1"); }} Результаты исполнения;
Метод вставки выполняется в первом потоке, что не приведет к тому, что второй поток заблокирует метод INSERT1.
Давайте посмотрим на то, что делает синхронизированное ключевое слово. Давайте декомпилируем его байт -код. Декомпилированный байт -код следующего кода:
открытый класс insertData {private Object object = new Object (); public void вставка (потока потока) {Synchronized (Object) {}} public Synchronized void insert1 (поток потока) {} public void insert2 (поток потока) {}}Из байт -кода, полученного путем разминирования, можно видеть, что блок синхронизированного кода фактически имеет две инструкции: Monitorenter и Monitorexit. Когда инструкция мониторинга будет выполнена, количество блокировки объекта будет увеличено на 1, в то время как при выполнении инструкции по мониторексуату, количество блокировки объекта будет уменьшено на 1. Фактически, это очень похоже на операцию PV в операционной системе. Работа PV в операционной системе используется для управления несколькими потоками для доступа к критическим ресурсам. Для синхронизированного метода поток в выполнении распознает, имеет ли структура Method_info метода настройку флага ACC_SYNCHRONIZE, а затем автоматически снимает блокировку объекта, вызывает метод и, наконец, выпускает блокировку. Если происходит исключение, поток автоматически выпускает блокировку.
Обратите внимание: для синхронизированных методов или синхронизированных кодовых блоков, когда происходит исключение, JVM автоматически отпустит блокировку, занятую текущим потоком, поэтому не будет тупика из -за исключения.
3. Некоторые другие примечательные вещи о синхронизировании
1. Разница между синхронизированным и статическим синхронизированным
Синхронизированный блокирует текущий экземпляр класса, чтобы предотвратить доступ к другим потокам со всеми синхронизированными блоками экземпляра класса одновременно. Обратите внимание, что это «текущий экземпляр класса», и нет такого ограничения в двух разных случаях класса. Затем статический синхронизированный контролирует доступ ко всем случаям класса. Статический синхронизированный ограничивает потоки для доступа к всем экземплярам класса в JVM одновременно и быстро доступ к соответствующему коду. Фактически, если в классе есть синхронизирован в методе или кодовом блоке, то после генерации экземпляра этого класса класс будет иметь быстрый мониторинг, и одновременный доступ поток к модифицированному синхронизированному экземпляру быстро защищается. Статический синхронизированный является публичным мониторингом, который является разницей между ними. То есть синхронизированный эквивалентен этому. Синхронизированный, а статический синхронизированный эквивалентен чем -то. Синхронизировано.
Японский автор, «Джава -картинка с многопоточным дизайном» Цзе Ченгао, имеет такую колонку, как это:
Pulbic Class Something () {public synchronized void issynca () {} public synchronized void issyncb () {} public static synchronized void csynca () {} public static synchronized void csyncb () {}} Тогда, если добавлены два экземпляра A и B чего -то класса, почему к следующей группе методов можно получить доступ к более чем одним потоком? axissynca () и x.issyncb ()
bxissynca () и y.issynca ()
cxcsynca () и y.csyncb ()
dxissynca () и что -то.csynca ()
Здесь ясно, что это можно судить:
A, оба доступ к синхронизированному домену одного и того же экземпляра, поэтому к нему нельзя получить одновременно. B предназначен для разных случаев, поэтому его можно получить одновременно. Поскольку он статичный синхронизирован, разные случаи все еще будут ограничены, что эквивалентно чем -то. Issynca () и что -то.sisyncb (), поэтому к нему нельзя получить одновременно.
Итак, как насчет D?, Ответ в книге можно получить одновременно. Причина ответа заключается в том, что синхронно заключается в том, что метод экземпляра и метод синхронного класса отличаются от блокировки.
Личный анализ означает, что синхронизированные и статические синхронизированные эквивалентны двум бандам, каждая из которых имеет свой контроль, и нет никаких ограничений друг на друга и может быть доступен одновременно. Неясно, насколько синхронно реализовано во внутреннем дизайне Java.
Вывод: A: Синхронизированный статический - это область определенного класса. Синхронизированный статический csync {} предотвращает доступ к нескольким потокам доступа к синхронизированному статическому методу в этом классе одновременно. Он работает на всех экземплярах объекта класса.
B: Синхронизированный - это область применения экземпляра. Синхронизированный issync () {} предотвращает доступ к нескольким потокам добраться до синхронизированного метода в этом случае одновременно.
2. Разница между синхронизированным методом и синхронизированным кодом быстро
Не существует различий между синхронизированными методами () {} и синхронизированным (this) {}, но синхронизированные методы () {} удобно для понимания прочитанного, в то время как синхронизированный (это) {} может более точно контролировать области конфликта, а иногда и эффективно выполнять.
3. Синхронизированное ключевое слово нельзя унаследовать