0. Относительно синхронизации потоков (1) Зачем нам синхронизировать многопоточное?
Синхронизация потоков относится к тому, чтобы позволить нескольким запущенным потокам хорошо сотрудничать вместе, чтобы позволить нескольким потокам разумно занимать и выпускать ресурсы по мере необходимости. Мы используем кодовые блоки синхронизации и методы синхронизации в Java для достижения этой цели. Например, решить проблему многопоточного нефиксированного выполнения порядка:
открытый класс TwothReadTest {public static void main (string [] args) {thread th1 = new mythread1 (); Thread Th2 = новый mythread2 (); th1.start (); th2.start (); }} класс mythread2 extends thread {@Override public void run () {for (int i = 0; i <10; i ++) Система. out.println ("Thread 1 счетчик:"+i); }} класс mythread1 extends thread {@Override public void run () {for (int i = 0; i <10; i ++) Система. out.println ("Thread 1 счетчик:"+i); }} класс mythread1 extends thread {@Override public void run () {for (int i = 0; i <10; i ++) Система. out.println ("Thread 2 счетчик:"+i); }}Результатом многопоточного выполнения в этом состоянии является случайная вставка выполнения по желанию, что полностью зависит от планирования потоков JVM. Во многих случаях, когда требуется упорядоченное выполнение, это случайное состояние выполнения, очевидно, не подходит.
public Class Threadtest {public static void main (string [] args) {mythread thread = new mythread (); Потока th1 = новый поток (поток); Потока th2 = новый поток (поток); th1.start (); th2.start (); }} класс Mythread реализует Runnable {@Override public synchronized void run () {for (int i = 0; i <10; i ++). out.println (Thread. CurrentThread (). getName ()+"счетчик:"+i); }}После использования метода синхронизации мы можем управлять потоком, чтобы исключительно занимать объект тела выполнения. Таким образом, во время процесса выполнения поток может выполнять задачи в корпусе выполнения одновременно и выйти из состояния блокировки. Затем JVM отправляет еще один поток, чтобы запустить задачи в органе выполнения за один раз.
(2) Парадигма для создания и запуска потоков:
В прошлом у нас также была собственная парадигма программирования для создания и запуска. Как правило, мы определили класс выполнения для переписывания метода run (), но этот метод соединяет орган выполнения и выполненные задачи вместе, что не способствует отделению с точки зрения разработки программного обеспечения. Выполнение потока означает, что поток выполняет задачу объекта через объект выполнения. С этой точки зрения, отделение назначения задачи от класса выполнения может прояснить различные роли многопоточного программирования и, таким образом, получить хорошую развязку. Ниже приведена парадигма программирования для создания и выполнения потока:
открытый класс formalthreadclass {public static void main (string [] args) {thread Think = новый поток (новый myrunnable ()); Thread.Start (); }} класс MyRunnable Refrancements Runnable {myTask myTask = new myTask (); @Override public void run () {mytask.dotask (); }} класс mytask {public void dotask () {System. out.println («Это настоящая задача»); }}
1. Синхронизированный принцип
В Java каждый объект имеет и имеет только одну блокировку синхронизации. Это также означает, что блокировка синхронизации существует на объекте.
Когда мы называем синхронизированный метод объекта, мы получаем блокировку синхронизации объекта. Например, синхронизированный (OBJ) приобретает блокировку синхронизации «объекта OBJ».
Доступ к блокировке синхронизации различными потоками является взаимно эксклюзивным. Другими словами, в определенный момент времени блокировка синхронизации объекта может быть получена только одним потоком! Через блокировки синхронизации мы можем достичь взаимоисключающего доступа к «объектам/методам» в нескольких потоках. Например, теперь есть два потока A и поток B, которые оба получают доступ «синхронная блокировка объекта obj». Предположим, что в какой -то момент резьба А приобретает «блокировку синхронизации OBJ» и выполняет некоторые операции; В настоящее время нить B также пытается получить «блокировку синхронизации OBJ» - нить B не может приобрести, он должен подождать, пока нить не выпустит «блокировку синхронизации OBJ» и может быть запущено.
2. Синхронизированные основные правила
Мы суммируем основные правила синхронизации в следующие 3 и проиллюстрируем их с помощью примеров.
Статья 1: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки будут заблокированы от доступа к «синхронизированному методу» или «синхронизированный кодовый блок» «объекта».
Статья 2: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки все равно могут получить доступ к асинхронизированному кодовому блоку «этот объект».
Статья 3: Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки будут заблокированы от доступа к другим «синхронизированным методам» или «синхронизированным кодовым блоком» «объекта».
(1) Статья 1:
Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки будут заблокированы от доступа к «синхронизированному методу» или «синхронизированный кодовый блок» «объекта». Ниже приведена демонстрационная программа, соответствующая «синхронизированному кодовому блоку».
класс MyRunable Reflsements Runnable {@Override public void run () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName () + "loop" + i); }} catch (прерывание Exception IE) {}}}} открытый класс demo1_1 {public static void main (string [] args) {runnable demo = new myrunable (); // Создать новый поток "заполненного объекта" T1 = новый поток (Demo, "T1"); // Создать новый поток T1 », T1 основан на потоке выполняемого объекта T2 = новый поток (Demo,« T2 »); // Создать новый «Поток T2», T2 основан на выполняемом объекте T1.Start (); // запустить «потока t1» t2.start (); // запустить "Поток T2"}} Результаты работы:
T1 Loop 0t1 Loop 1T1 Loop 2T1 Loop 3T1 LOOP 4T2 LOOP 0T2 LOOP 1T2 LOOP 2T2 LOOP 3T2 LOOP 4
Результат показывает, что в методе run () существует «синхронизированный (этот) кодовый блок», а T1 и T2 - это потоки, созданные на основе «демонстрационного» выполнения объекта. Это означает, что мы можем рассматривать это синхронизированным (это) как «демонстрационный объект»; Следовательно, потоки T1 и T2 используют «синхронную блокировку демо -объекта». Следовательно, когда работает один поток, другой поток должен ждать, пока «запущенный поток» выпустит «блокировку демо -синхронизации», прежде чем он сможет запустить.
Если вы подтвердите, вы поняли эту проблему. Затем мы изменяем приведенный выше код, а затем запускаем его, чтобы увидеть, какова результат, и посмотрим, будут ли вы запутаны. Модифицированный исходный код выглядит следующим образом:
класс mythread extends thread {public mythread (string name) {super (name); } @Override public void run () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName () + "loop" + i); }} catch (прерывание Exception IE) {}}}} открытый класс demo1_2 {public static void main (string [] args) {Thread T1 = new Mythread ("t1"); // Создать новую поток T1 T -TEHD T2 = NEW MYTHREAD ("T2"); // Создать новый «потока t2» t1.start (); // запустить «потока t1» t2.start (); // запустить "Поток T2"}} Описание кода: Сравнение DEMO1_2 и DEMO1_1, мы обнаружили, что класс MyThread в DEMO1_2 непосредственно унаследован от потока, а T1 и T2 - это детские потоки Mythread.
К счастью, метод «run () demo1_2» также называется синхронизированным (это), так же как метод «run () demo1_1», также называемый синхронизированным (это)!
Итак, процесс выполнения DEMO1_2 таким же, как DEMO1_1? Результаты работы:
T1 Loop 0t2 Loop 0t1 Loop 1T2 Loop 1T1 Loop 2T2 LOOP 2T1 LOOP 3T2 LOOP 3T1 LOOP 4T2 LOOP 4
Результаты Описание:
Если этот результат вас совсем не удивляет, то я считаю, что у вас есть более глубокое понимание синхронизации и этого. В противном случае, пожалуйста, продолжайте читать анализ здесь.
Это синхронизированное (это) относится к «объекту текущего класса», то есть текущего объекта, соответствующего классу, где находится синхронизированный (это). Его цель состоит в том, чтобы получить «синхронную блокировку текущего объекта».
Для Demo1_2 это синхронизированное (это) представляет объект Mythread, а T1 и T2 - два разных объекта Mythread. Следовательно, когда T1 и T2 выполняют синхронизированный (это), они приобретают блокировки синхронизации разных объектов. Для пары Demo1_1 это синхронизированное (это) представляет собой мирруемый объект; T1 и T2 делятся общедоступным объектом. Следовательно, один поток приобретает блокировку синхронизации объекта, что приведет к тому, что другой поток ждает.
(2) Статья 2:
Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», другие потоки могут по -прежнему получить доступ к асинхронизированному блоку кода «этот объект».
Ниже приведена демонстрационная программа, соответствующая «синхронизированному кодовому блоку».
Class Count {// Методы, содержащие синхронизированную синхронизацию, блокирует public void synmethod () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep для 100 мс System.out.println (Thread.currentThread (). GetName () + "Synmethod Loop" + I); }} catch (прерывание Exception IE) {}}} // асинхронный метод public void nonsynmethod () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); System.out.println (Thread.currentThread (). GetName () + "nonsynmethod loop" + i); }} catch (прерывание Exception IE) {}}} открытый класс demo2 {public static void main (string [] args) {final count count = new count (); // Создать новый T1, T1 будет вызывать метод synmethod () поток «count object» t1 = new Thread (new Runnable () {@override public void run () {count.synmethod ();}}, "t1"); // Создать новый T2, T2 будет вызывать метод nonsynmethod () поток «count object» t2 = new Thread (new Runnable () {@Override public void run () {count.nonsynmethod ();}}, "t2"); t1.start (); // запустить t1 t2.start (); // запустить t2}} Результаты работы:
T1 Synmethod Loop 0t2 Синсинметод петля 0T1 Синметод петля 1T2 Синсинметод петля 1T1 Синметод петля 2T2 Безинсинметод петля 2T1 Синметод петля 3T2 nonsynmethod петля 3T1 Loop 4t2 nonsynmethod loop 4
Результаты Описание:
Две новые детские потоки T1 и T2 создаются в основном потоке. T1 вызовет метод Synmethod () объекта Count, который содержит блоки синхронизации; T2 вызовет метод nonsynmethod () объекта Count, который не является методом синхронизации. Когда T1 работает, хотя синхронизированный (это) вызывается для получения «блокировки синхронизации графа»; Это не заставляет T2 блокировать, потому что T2 не использует блокировку синхронизации «счета».
(3) Статья 3:
Когда поток обращается к «синхронизированному методу» или «синхронизированному кодовому блоку» «определенного объекта», будут заблокированы другие потоки к другим «синхронизированным методам» или «синхронизированный код» «объекта».
Мы также будем изменять кузов метода nonsynmethod () в приведенном выше примере с помощью синхронизированного (это). Модифицированный исходный код выглядит следующим образом:
Class Count {// Методы, содержащие синхронизированную синхронизацию, блокирует public void synmethod () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep для 100 мс System.out.println (Thread.currentThread (). GetName () + "Synmethod Loop" + I); }} catch (прерывание Exception IE) {}}} // Методы, содержащие синхронизированную синхронизацию, блокируют public void nonsynmethod () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); System.out.println (Thread.currentThread (). GetName () + "nonsynmethod loop" + i); }} catch (прерывание Exception IE) {}}}} открытый класс demo3 {public static void main (string [] args) {final count count = new count (); // Создание T1, T1 вызовет метод Synmethod () поток «count yem» T1 = новый поток (new Runnable () {@Override public void run () {count.syncmethod ();}}, "t1"); // Создать новый T2, T2 вызовет метод nonsynmethod () метода «count object» TENTE T2 = новый поток (new Runnable () {@Override public void run () {count.nonsynmethod ();}}, "t2"); t1.start (); // запустить t1 t2.start (); // запустить t2}} Результаты работы:
Синметод Synmethod Loop 0t1 Синметод петля 1T1 Синметод петля 2T1 Синметод петля 3T1 Синметод петля 4T2 Бессинметод петля 0T2 Несооооооооооооооп 1T2 LOOP 4 -й петль 4
Результаты Описание:
Две новые детские потоки T1 и T2 создаются в основном потоке. Как T1, так и T2 -синхронизированный (это), который является объектом Count (Count), и CORT T1 и T2. Следовательно, когда T1 работает, T2 будет заблокирован, и T1 будет запущен, чтобы выпустить «синхронную блокировку объекта Count» до того, как T2 может запустить.
3. Синхронизированный метод и синхронизированный кодовый блок
«Синхронизированный метод» использует метод синхронизированной модификации, в то время как «блок синхронизированного кода» использует блок синхронизированного кода модификации.
Пример синхронизированного метода
public Synchronized void foo1 () {System.out.println ("Синхронизированный метод");} синхронизированный кодовый блок public void foo2 () {synchronized (this) {System.out.println ("синхронизированный метод"); }} Это в синхронном блоке кода относится к текущему объекту. Это также может быть заменено другими объектами, такими как это заменяется OBJ, затем FOO2 () приобретает блокировку синхронизации OBJ при синхронизации (OBJ).
Синхронизированные кодовые блоки могут более точно контролировать области доступа к конфликту более точно, а иногда и более эффективно. Вот пример для демонстрации:
// demo4.java исходный код открытого класса Demo4 {public synchronized void synmethod () {for (int i = 0; i <1000000; i ++); } public void synblock () {synchronized (this) {for (int i = 0; i <1000000; i ++); }} public static void main (string [] args) {demo4 demo = new demo4 (); долгое начало, diff; start = System.CurrentTimeMillis (); // Получить текущее время (миллисы) demo.syncmethod (); // Вызов "Синхронизированный метод" diff = System.currentTimeMillis () - start; // Получить "разницу в времени" system.out.println ("syncmethod ():"+ diff); start = System.CurrentTimeMillis (); // получить текущее время (миллисы) demo.syncblock (); // вызов "Синхронизированный метод блока" diff = system.currenttimemillis () - start; // Получить "разницу в времени" system.out.println ("syncblock ():"+ diff); }} (Один раз) Результат выполнения:
Synmethod (): 11SynBlock (): 3
4. Замок экземпляра и глобальная блокировка
Замок экземпляра-заблокирован на объекте экземпляра. Если класс является синглтоном, то замок также имеет концепцию глобального блокировки.
(1) Синхронизированное ключевое слово соответствует блокировке экземпляра.
(2) Глобальный блокировка-блокировка нацелена на класс. Независимо от того, сколько объектов экземпляр, потоки делятся блокировкой.
Глобальная блокировка соответствует статическому синхронизированию (или заблокированной на объекте класса или класса загрузчика этого класса).
Существует очень яркий пример «блокировки экземпляра» и «глобального блокировки»:
Pulbic Class Something {public synchronized void issynca () {} public synchronized void issyncb () {} public static synchronized void csynca () {} public static synchronized void csyncb () {}} Предположим, что -то имеет два случая x и y. Проанализируйте замки, полученные следующими четырьмя наборами выражений.
(1) x.issynca () и x.issyncb ()
(2) x.issynca () и y.issynca ()
(3) x.csynca () и y.csyncb ()
(4) x.issynca () и что -то.csynca ()
(1) не может быть доступен одновременно.
Потому что issynca () и issyncb () являются блокировками синхронизации, которые получают доступ к одному и тому же объекту (объект x)!
// lockTest1.java's Source Code Class Something {public Synchronized void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issynca"); }} catch (прерывание Exception IE) {}} public synchronized void issyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep для 100 мс System.out.println (thread.currentThread (). GetName ()+": issyncb"); }} catch (прерывание Exception IE) {}}} открытый класс LockTest1 {что -то x = new Something (); Что -то y = новое что -то (); // сравнить (01) x.issynca () с x.issyncb () private void test1 () {// Создать новый T11, T11 вызовет x.issynca () поток t11 = new Thread (new Runnable () {@override public void run () {x.issynca ();}}, "T11"); // Создание нового T12, T12 будет вызовом x.issyncb () поток t12 = new Thread (new Runnable () {@Override public void run () {x.issyncb ();}}, "T12"); t11.start (); // запустить t11 t12.start (); // запустить t12} public static void main (string [] args) {lockTest1 demo = new LockTest1 (); demo.test1 (); }} Результаты работы:
T11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCB
(2) можно получить одновременно
Поскольку он не доступ к блокировке синхронизации одного и того же объекта, x.issynca () обращается к блокировке синхронизации x, в то время как y.issynca () обращается к блокировке синхронизации y.
// lockTest2.java исходный код кода что -то {public synchronized void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issynca"); }} catch (прерывание Exception IE) {}} public synchronized void issyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issyncb"); }} catch (прерывание Exception IE) {}} public static synchronized void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (прерывание Exception IE) {}} public static synchronized void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName ()+": csyncb"); }} catch (прерывание Exception IE) {}}} открытый класс LockTest2 {что -то x = new Something (); Что -то y = новое что -то (); // сравнить (02) x.issynca () с y.issynca () private void test2 () {// Создать новый T21, T21 вызовет x.issynca () поток T21 = новый поток (новый Runnable () {@Override public void run () {x.issynca ();}}, "t21"); // Создать новый T22, T22 вызовет x.issyncb () поток t22 = new Thread (new Runnable () {@Override public void run () {y.issynca ();}}, "T22"); t21.start (); // запустить t21 t22.start (); // запуск t22} public static void main (string [] args) {lockTest2 demo = new LockTest2 (); demo.test2 (); }} Результаты работы:
T21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCA
(3) нельзя получить одновременно
Поскольку csynca () и csyncb () являются статическими типами, x.csynca () эквивалентен чем -то. Issynca (), а y.csyncb () эквивалентен чем -то.
// lockTest3. класс исходного кода Java что -то {public synchronized void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issynca"); }} catch (прерывание Exception IE) {}} public synchronized void issyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issyncb"); }} catch (прерывание Exception IE) {}} public static synchronized void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (прерывание Exception IE) {}} public static synchronized void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName ()+": csyncb"); }} catch (прерывание Exception IE) {}}} открытый класс LockTest3 {что -то x = new Something (); Что -то y = новое что -то (); // Сравнение (03) x.csynca () с y.csyncb () private void test3 () {// Создать новый T31, T31 вызовет x.issynca () поток T31 = новый поток (new Runnable () {@override public void run () {x.csynca ();}}, "t31");); // Создать новый T32, T32 будет вызовом x.issyncb () поток T32 = новый поток (new Runnable () {@Override public void run () {y.csyncb ();}}, "T32"); t31.start (); // запустить T31 T32.start (); // запуск t32} public static void main (string [] args) {lockTest3 demo = new LockTest3 (); demo.test3 (); }} Результаты работы:
t31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: CSYNCB
(4) можно получить одновременно
Поскольку issynca () является методом экземпляра, x.issynca () использует блокировку объекта x; В то время как csynca () является статическим методом, что -то. Csynca () может понять, что это используется «блокировка класса». Следовательно, к ним можно получить доступ одновременно.
// lockTest4.java's Source Code Class Something {public Synchronized void issynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issynca"); }} catch (прерывание Exception IE) {}} public synchronized void issyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (thread.currentThread (). GetName ()+": issyncb"); }} catch (прерывание Exception IE) {}} public static synchronized void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100 мс System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (прерывание Exception IE) {}} public static synchronized void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep для 100 мс System.out.println (thread.currentThread (). GetName ()+": csyncb"); }} catch (прерывание Exception IE) {}}} открытый класс LockTest4 {что -то x = new Something (); Что -то y = новое что -то (); // сравнить (04) x.issynca () с чем -то. Csynca () private void test4 () {// Создать новый T41, T41 вызовет x.issynca () поток T41 = новый поток (новый Runnable () {@Override public void run () {x.issynca ();}}, "t41"); // Создание нового T42, T42 будет вызовом x.issyncb () поток t42 = new Thread (new Runnable () {@Override public void run () {что -то.csynca ();}}, "T42"); t41.start (); // запустить t41 t42.start (); // запуск t42} public static void main (string [] args) {lockTest4 demo = new LockTest4 (); demo.test4 (); }} Результаты работы:
T41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCA