Перед Java 5 синхронизированное ключевое слово использовалось для реализации функции блокировки.
Синхронизированное ключевое слово можно использовать в качестве модификатора (синхронизированный метод) или в качестве оператора в функции (синхронизированный кодовый блок).
Чтобы освоить синхронизированный, ключ состоит в том, чтобы овладеть использованием этой вещи в качестве блокировки. Для нестатических методов (методов членов) класса это означает получить блокировку экземпляра объекта; Для статических методов (методов класса) класса необходимо получить блокировку объекта класса; Для синхронных кодовых блоков необходимо указать блокировку объекта. Синхронизированные нестатические методы могут рассматриваться как синхронизированный (this) {…} кодовый блок, содержащий весь метод.
Независимо от того, является ли это синхронным кодовым блоком или методом синхронизации, только один поток может входить за раз (максимум, один поток выполняет сегмент кода одновременно.), И если другие потоки пытаются войти (будь то один и тот же синхронный блок или другой блок синхронизации), JVM повесит их (положить в пул блокировки ожидания). Эта структура называется критическим разделом в теории параллелистики.
В JVM, чтобы повысить эффективность, каждый поток, работающий одновременно, будет иметь копию кэша данных, которые он обрабатывает. Когда мы используем синхронность для синхронизации, действительно синхронизируется блок памяти, представляющий заблокированный объект в разных потоках (данные копирования останутся синхронизированными с основной памятью. Теперь мы знаем, почему используется синхронизация слова). Проще говоря, после выполнения блока синхронизации или метода синхронизации любые модификации, сделанные в заблокированный объект, должны быть записаны обратно в основную память, прежде чем выпустить блокировку; После входа в блок синхронизации и получения блокировки данные заблокированного объекта считываются из основной памяти, а копия данных, содержащего блокировку, должна быть синхронизирована с представлением данных в основной памяти.
Ниже приведены конкретные примеры, чтобы проиллюстрировать различные ситуации синхронизации.
Синхронизированный метод синхронизации
Во -первых, давайте посмотрим на пример метода синхронизации:
Public Class SynchronizedTest1 Extends Thread {Private Synchronized void TestSynchronizedMethod () {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). getName () + "testSynchronizedMethod:" + i); try {thread.sleep (100); } catch (прерванное искусство e) {e.printstacktrace (); }}} @Override public void run () {testSynchronizedMethod (); } public static void main (string [] args) {synchronizedTest1 t = new SynchronizedTest1 (); t.start (); t.testsynchronizedmethod (); }}Запуск выходов программы:
Основной TestSynchronizedMethod: 0 Основной TestSynchronizedMethod: 1 Основной TestSynchronizedMethod: 2 Основной TestSynchronizedMethod: 3 Основной TestSynchronizedMethod: 4 Основной TestSynchronizedMethod: 5 MainsynchronizedMethod: 6 MainsynchronizedMethod: 7 Main TestSynchroncethod: 6 MainsynchronizedMethod: 7 Mainessynchronmethod. testSynchronizedMethod:9 Thread-0 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-0 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-0 TestSynchronizedMethod: 7 Thread-0 testSynchronizedMethod: 8 Thread-0 testSynchronizedMethod: 9
Вы можете видеть, что метод TestSynchronizedMethod выполняется синхронно между двумя потоками.
Если основной метод изменен в следующем, два потока не могут выполнять синхронно, потому что монитор синхронизации двух потоков не является одним и тем же объектом и не может играть синхронную роль.
public static void main (string [] args) {thread t = new SynchronizedTest1 (); t.start (); Потока t1 = new SynchronizedTest1 (); t1.start (); }Результат вывода заключается в следующем:
Thread-0 testSynchronizedMethod:0 Thread-1 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-1 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-1 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-1 testSynchronizedMethod:3 Thread-0 testSynchronizedMethod:4 Thread-1 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-1 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-1 testSynchronizedMethod:6 Thread-0 testSynchronizedMethod:7 Thread-1 testSynchronizedMethod:7 Thread-0 TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 8 Thread-0 TestSynchronizedMethod: 9 Thread-1 TestSynchronizedMethod: 9
Если модифицированный основной метод может быть синхронно запускать между двумя потоками, метод TestSynchronizedMethod должен быть объявлен как статический метод, чтобы мониторы двух потоков являются одним и тем же объектом (объект класса) и могут выполняться синхронно. Модифицированный код выглядит следующим образом:
public Class SynchronizedTest1 extends Thread {Private Static Synchronized void testSynchronizedmethod () {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). getName () + "testSynchronizedMethod:" + i); try {thread.sleep (100); } catch (прерванное искусство e) {e.printstacktrace (); }}} @Override public void run () {testSynchronizedMethod (); } public static void main (string [] args) {thread t = new SynchronizedTest1 (); t.start (); Потока t1 = new SynchronizedTest1 (); t1.start (); }}Результат вывода заключается в следующем:
Thread-0 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-0 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-0 testSynchronizedMethod:7 Thread-0 testSynchronizedMethod:8 Thread-0 testSynchronizedMethod:9 Thread-1 testSynchronizedMethod:0 Thread-1 testSynchronizedMethod:1 Thread-1 testSynchronizedMethod:2 Thread-1 testSynchronizedMethod:3 Thread-1 testSynchronizedMethod:4 Thread-1 testSynchronizedMethod:5 Thread-1 testSynchronizedMethod:6 Thread-1 TestSynchronizedMethod: 7 Thread-1 TestSynchronizedMethod: 8 Thread-1 TestSynchronizedMethod: 9
Ситуация синхронных блоков аналогична методу синхронизации, за исключением того, что синхронный блок снижает детализацию контроля синхронизации, что может лучше повысить эффективность многопоточного параллельного выполнения.
Используйте этот объект для управления синхронизацией между теми же экземплярами объекта:
Public Class SynchronizedTest2 Extends Thread {private void testSynchronizedblock () {synchronized (this) {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). getName () + "testSyNchronizedBlock:" + i); try {thread.sleep (100); } catch (прерванное искусство e) {e.printstacktrace (); }}}} @Override public void run () {testSynchronizedBlock (); } public static void main (string [] args) {synchronizedTest2 t = new SynchronizedTest2 (); t.start (); t.testsynchronizedblock (); }}Результат вывода:
Основной TestSynchronizedBlock: 0 Основной TestSynchronizedBlock: 1 Основной TestSynchronizedBlock: 2 Основной TestSynchronizedBlock: 3 MainSynchronizedBlock: 4 Основной TestSynchronizedBlock: 5 MainSynchronicalblock: 6 Mainsynchronichrockblock: 7 Mainsynchronchronblock: 8 MainsynchnchnchnchnchnchnichrynChroclocloclocloclocloclocloclocloclocloc testSynchronizedBlock:0 Thread-0 testSynchronizedBlock:1 Thread-0 testSynchronizedBlock:2 Thread-0 testSynchronizedBlock:3 Thread-0 testSynchronizedBlock:4 Thread-0 testSynchronizedBlock:5 Thread-0 testSynchronizedBlock:6 Thread-0 testSynchronizedBlock:7 Thread-0 testSynchronizedBlock:8 Thread-0 TestSynchronizedBlock: 9
Используйте объекты класса для управления синхронизацией между различными случаями:
public Class SynchronizedTest2 extends Thread {private void testSynchronizedBlock () {synchronized (synchronizedTest2.class) {for (int i = 0; i <10; i ++) {System.out.println (Thread.currentThread (). getName () + "testSynchronicdblock:" + i); try {thread.sleep (100); } catch (прерванное искусство e) {e.printstacktrace (); }}}} @Override public void run () {testSynchronizedBlock (); } public static void main (string [] args) {thread t = new SynchronizedTest2 (); t.start (); Thread T2 = new SynchronizedTest2 (); t2.start (); }}Результат вывода:
Thread-0 testSynchronizedBlock:0 Thread-0 testSynchronizedBlock:1 Thread-0 testSynchronizedBlock:2 Thread-0 testSynchronizedBlock:3 Thread-0 testSynchronizedBlock:4 Thread-0 testSynchronizedBlock:5 Thread-0 testSynchronizedBlock:6 Thread-0 testSynchronizedBlock:7 Thread-0 testSynchronizedBlock:8 Thread-0 testSynchronizedBlock:9 Thread-1 testSynchronizedBlock:0 Thread-1 testSynchronizedBlock:1 Thread-1 testSynchronizedBlock:2 Thread-1 testSynchronizedBlock:3 Thread-1 testSynchronizedBlock:4 Thread-1 testSynchronizedBlock:5 Thread-1 testSynchronizedBlock:6 Thread-1 testSynchronizedBlock:7 Thread-1 TestSynchronizedBlock: 8 Thread-1 TestSynchronicizedBlock: 9
При использовании синхронизированного ключевого слова для управления синхронизацией необходимо понять монитор объекта. Только процесс, который получает монитор, может работать, и все остальное нужно ждать, чтобы получить монитор. Любой не нулевой объект может использоваться в качестве монитора объекта. Когда синхронизируют действия на методе, экземпляр объекта заблокирован; Действуя на статическом методе, экземпляр объекта заблокируется, соответствующий объекту.
Синхронный метод для двух потоков для доступа к объекту одновременно
Когда два параллельных потока получают доступ к синхронному методу одного и того же объекта, можно выполнить только один поток. Другой поток должен ждать, пока текущий поток выполнит его, прежде чем его можно будет выполнить.
открытый класс Twothread {public static void main (string [] args) {final twothread twothread = new twothread (); Поток t1 = new Thread (new Runnable () {public void run () {twothread.syncmethod ();}}, "a"); Поток t2 = new Thread (new Runnable () {public void run () {twothread.syncmethod ();}}, "b"); t1.start (); t2.start (); } public void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); try {thread.sleep (500); } catch (прерванное искусство т.е.) {}}}}Результат вывода:
A: 0a: 1a: 2a: 3a: 4b: 0b: 1b: 2b: 3b: 4
Метод синхронизации двух объектов доступ к двум потокам
В этом случае синхронизированный не работает, как и обычный метод. Потому что соответствующие замки являются их соответствующими объектами.
public Class TwoObject {public static void main (string [] args) {final TwoObject object1 = new TwoObject (); Поток t1 = new Thread (new Runnable () {public void run () {object1.syncmethod ();}}, "Object1"); t1.start (); final TwoObject object2 = new TwoObject (); Поток t2 = new Thread (new Runnable () {public void run () {public void run () {object2.syncmethod ();}}, "Object2"); t2.start (); } public void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); try {thread.sleep (500); } catch (прерванное искусство т.е.) {}}}}Один из возможных выходов:
Объект2: 0object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
Две потоки получают доступ к синхронизированному статическому методу
В этом случае, поскольку класс заблокирован, в любое время, только один поток может выполнить статический метод.
Доступ к синхронным методам и асинхронным методам одновременно, когда один поток обращается к одному методу синхронизации объекта, другой поток все еще может получить доступ к асинхронным методам в этом объекте.
открытый класс SyncandNosync {public static void main (String [] args) {final SyncandNosync SyncandNosync = new SyncandNosync (); Поток t1 = new Thread (new Runnable () {public void run () {syncandnosync.syncmethod ();}}, "a"); t1.start (); Поток t2 = new Thread (new Runnable () {public void run () {syncandnosync.nosyncmethod ();}}, "b"); t2.start (); } public void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "At SyncMethod ():" + i); try {thread.sleep (500); } catch (прерывание Exception IE) {}}} public void nosyncmethod () {for (int i = 0; i <5; i ++) {System.out.println (Thread.currentThread (). getName () + "at nosyncmethod ():" + i); try {thread.sleep (500); } catch (прерванное искусство т.е.) {}}}}Один возможный выход:
B at nosyncmethod (): 0a at at syncmethod (): 0b at nosyncmethod (): 1a at at syncmethod (): 1b at nosyncmethod (): 2a при syncmethod (): 2b at nosyncmethod (): 3a at syncmethod (): 3a at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at at syncmethod: nosyncmethod (): 4
Различные методы синхронизации для доступа к одному и тому же объекту
Когда поток обращается к методу синхронизации A объекта, другие потоки получают доступ ко всем другим методам синхронизации в объекте, будут заблокированы. Поскольку первый поток получил блокировку объекта, а другие потоки не могут получить блокировку, хотя он доступ к другому методу, он не получает блокировку и не может получить к нему доступ.
открытый класс Twosyncmethod {public static void main (string [] args) {final twosyncmethod twosyncmethod = new twosyncmethod (); Поток t1 = new Thread (new Runnable () {public void run () {twosyncmethod.syncmethod1 ();}}, "a"); t1.start (); Поток t2 = new Thread (new Runnable () {public void run () {twosyncmethod.syncmethod2 ();}}, "b"); t2.start (); } public synchronized void syncmethod1 () {for (int i = 0; i <5; i ++) {System.out.println (Thread.currentThread (). getName () + "At Syncmethod1 ():" + i); try {thread.sleep (500); } catch (прерывание Exception IE) {}}} public synchronized void syncmethod2 () {for (int i = 0; i <5; i ++) {system.out.println (thread.currentThread (). getName () + "atsmethod2 ():" + i); try {thread.sleep (500); } catch (прерванное искусство т.е.) {}}}}Результат вывода:
A at syncmethod1 (): 0a at at syncmethod1 (): 1a at at syncmethod1 (): 2a at at syncmethod1 (): 3a at syncmethod1 (): 4b при syncmethod2 (): 0b at syncmethod2 (): 1b at syncmethod2 (): 2b at at at at at at at at at at at at at at at at at at at at at at at at at at at at at syncmethod2 Syncmethod2 (): 4