синхронизированный блок кода
Объявление метода с ключевым словом synchronized в некоторых случаях имеет недостатки, например, когда поток A вызывает синхронизированный метод для выполнения задачи в течение длительного времени, поток B должен долго ждать. В этом случае для решения проблемы можно попробовать использовать синхронизированный блок синхронизации. Взгляните на пример:
Следующий пример представляет собой оптимизированный пример. Используйте блокировку блока кода. В исходном примере используется блокировка метода, то есть синхронизация. Необходимо выполнить два fors.
package org.java.base.sync;
public class ThreadDomain18
{
public void doLongTimeTask() throws Exception
{
for (int i = 0; i < 100; i++)
{
System.out.println("nosynchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
System.out.println();
synchronized (this)
{
for (int i = 0; i < 100; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
}
}
}
package org.java.base.sync;
public class MyThread18 extends Thread
{
private ThreadDomain18 td;
public MyThread18(ThreadDomain18 td)
{
this.td = td;
}
public void run()
{
try
{
td.doLongTimeTask();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
package org.java.base.sync;
public class Test1 {
public static void main(String[] args)
{
ThreadDomain18 td = new ThreadDomain18();
MyThread18 mt0 = new MyThread18(td);
MyThread18 mt1 = new MyThread18(td);
mt0.start();
mt1.start();
}
}
Результаты операции делятся на две части:
synchronized threadName = Thread-1, i = 1
synchronized threadName = Thread-1, i = 2
nosynchronized threadName = Thread-0, i = 95
synchronized threadName = Thread-1, i = 3
nosynchronized threadName = Thread-0, i = 96
synchronized threadName = Thread-1, i = 4
nosynchronized threadName = Thread-0, i = 97
synchronized threadName = Thread-1, i = 5
nosynchronized threadName = Thread-0, i = 98
synchronized threadName = Thread-1, i = 6
nosynchronized threadName = Thread-0, i = 99
synchronized threadName = Thread-1, i = 7
nosynchronized threadName = Thread-0, i = 100
...
synchronized threadName = Thread-1, i = 98
synchronized threadName = Thread-1, i = 99
synchronized threadName = Thread-1, i = 100
synchronized threadName = Thread-0, i = 1
synchronized threadName = Thread-0, i = 2
synchronized threadName = Thread-0, i = 3
...
Из этого эксперимента можно сделать два вывода:
1,Когда поток A получает доступ к синхронизированному блоку кода объекта, поток B по-прежнему может обращаться к остальным несинхронизированным блокам в объектном методе, что доказывает результат выполнения первой части.
2,Когда поток A входит в синхронизированный блок кода объекта, если поток B хочет получить доступ к этому синхронизированному блоку, доступ будет заблокирован, что доказывает результат выполнения второй части.
Поэтому, с точки зрения эффективности выполнения, иногда нам не нужно добавлять синхронизированный метод ко всему методу, но мы можем использовать метод синхронизированного блока для синхронизации той части кода, которая вызовет проблемы с безопасностью потоков.
Взаимное исключение между двумя синхронизированными блоками
Если поток 1 обращается к синхронизированному блоку метода объекта A, то доступ потока B к синхронизированному блоку того же метода объекта B будет заблокирован. Напишите пример, подтверждающий это:
public class ThreadDomain19
{
public void serviceMethodA()
{
synchronized (this)
{
try
{
System.out.println("A begin time = " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("A end time = " + System.currentTimeMillis());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public void serviceMethodB()
{
synchronized (this)
{
System.out.println("B begin time = " + System.currentTimeMillis());
System.out.println("B end time = " + System.currentTimeMillis());
}
}
}
Напишите два потока для вызова этих двух методов по отдельности:
public class MyThread19_0 extends Thread
{
private ThreadDomain19 td;
public MyThread19_0(ThreadDomain19 td)
{
this.td = td;
}
public void run()
{
td.serviceMethodA();
}
}
public class MyThread19_1 extends Thread
{
private ThreadDomain19 td;
public MyThread19_1(ThreadDomain19 td)
{
this.td = td;
}
public void run()
{
td.serviceMethodB();
}
}
Напишите основную функцию:
public static void main(String[] args)
{
ThreadDomain19 td = new ThreadDomain19();
MyThread19_0 mt0 = new MyThread19_0(td);
MyThread19_1 mt1 = new MyThread19_1(td);
mt0.start();
mt1.start();
}
Взгляните на беговые результаты:
A begin time = 1443843271982
A end time = 1443843273983
B begin time = 1443843273983
B end time = 1443843273983
Обратите внимание, что доступ к синхронизированному блоку метода serviceMethodB() должен ожидать окончания доступа к синхронизированному блоку метода serviceMethodA(). Собственно на этом примере мы тоже можем сделать вывод:Синхронизированный блок получает блокировку объекта, другими словами, синхронизированный блок блокирует весь объект.
синхронизированные блоки и синхронизированные методы
Поскольку сделанный выше выводСинхронизированный блок получает блокировку объекта, то если поток 1 обращается к синхронизированному блоку метода объекта A, доступ потока 2 к методу синхронизации B того же объекта должен быть заблокирован, поскольку поток 2 попытается получить это при доступе к методу синхронизации B того же объекта. объекта, но эта блокировка здесь, в потоке 1. Напишите пример, подтверждающий этот вывод:
public class ThreadDomain20
{
public synchronized void otherMethod()
{
System.out.println("----------run--otherMethod");
}
public void doLongTask()
{
synchronized (this)
{
for (int i = 0; i < 1000; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
try
{
Thread.sleep(5);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}
Напишите два потока для вызова этих двух методов по отдельности:
public class MyThread20_0 extends Thread
{
private ThreadDomain20 td;
public MyThread20_0(ThreadDomain20 td)
{
this.td = td;
}
public void run()
{
td.doLongTask();
}
}
public class MyThread20_1 extends Thread
{
private ThreadDomain20 td;
public MyThread20_1(ThreadDomain20 td)
{
this.td = td;
}
public void run()
{
td.otherMethod();
}
}
Напишите основную функцию для вызова, здесь «mt0.start()» после сна (100) следующее должно гарантировать, что поток mt0 запустится первым:
public static void main(String[] args) throws Exception
{
ThreadDomain20 td = new ThreadDomain20();
MyThread20_0 mt0 = new MyThread20_0(td);
MyThread20_1 mt1 = new MyThread20_1(td);
mt0.start();
Thread.sleep(100);
mt1.start();
}
Взгляните на беговые результаты:
...
synchronized threadName = Thread-0, i = 995
synchronized threadName = Thread-0, i = 996
synchronized threadName = Thread-0, i = 997
synchronized threadName = Thread-0, i = 998
synchronized threadName = Thread-0, i = 999
synchronized threadName = Thread-0, i = 1000
----------run--otherMethod
доказать наш вывод. Чтобы еще больше улучшить этот вывод, удалите синхронизацию метода «otherMethod()» и посмотрите на текущий результат:
...
synchronized threadName = Thread-0, i = 16
synchronized threadName = Thread-0, i = 17
synchronized threadName = Thread-0, i = 18
synchronized threadName = Thread-0, i = 19
synchronized threadName = Thread-0, i = 20
----------run--otherMethod
synchronized threadName = Thread-0, i = 21
synchronized threadName = Thread-0, i = 22
synchronized threadName = Thread-0, i = 23
...
Синхронизированные блоки в методе «otherMethod()» и методе «doLongTask()» выполняются асинхронно.
Используйте любой объект в качестве монитора объекта
Подводя итог предыдущему содержанию:
1, синхронизированный метод синхронизации
(1) Блокирование других синхронизированных методов синхронизации или синхронизированных (этих) кодовых блоков синхронизации
(2) Только один поток может выполнять код синхронизированного метода синхронизации одновременно.
2, синхронизированный блок кода синхронизации
(1) Блокирование других синхронизированных методов синхронизации или синхронизированных (этих) кодовых блоков синхронизации
(2) Только один поток может выполнять код в синхронизированном (этом) синхронизированном блоке кода одновременно.
Формат synchronized(this) используется для синхронизации блоков кода в начале.Java также поддерживает «произвольные объекты» в качестве мониторов объектов для достижения синхронизации.. Этот «произвольный объект» в основномпеременная экземпляраа такжепараметры метода, используя формат synchronized (не этот объект). Взгляните на пример использования произвольного объекта в качестве монитора объекта:
public class ThreadDomain21
{
private String userNameParam;
private String passwordParam;
private String anyString = new String();
public void setUserNamePassword(String userName, String password)
{
try
{
synchronized (anyString)
{
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在 " + System.currentTimeMillis() + " 进入同步代码块");
userNameParam = userName;
Thread.sleep(3000);
passwordParam = password;
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在 " + System.currentTimeMillis() + " 离开同步代码块");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
Напишите два потока для вызова по отдельности:
public class MyThread21_0 extends Thread
{
private ThreadDomain21 td;
public MyThread21_0(ThreadDomain21 td)
{
this.td = td;
}
public void run()
{
td.setUserNamePassword("A", "AA");
}
}
public class MyThread21_1 extends Thread
{
private ThreadDomain21 td;
public MyThread21_1(ThreadDomain21 td)
{
this.td = td;
}
public void run()
{
td.setUserNamePassword("B", "B");
}
}
Напишите основную функцию для вызова:
public static void main(String[] args)
{
ThreadDomain21 td = new ThreadDomain21();
MyThread21_0 mt0 = new MyThread21_0(td);
MyThread21_1 mt1 = new MyThread21_1(td);
mt0.start();
mt1.start();
}
Взгляните на беговые результаты:
线程名称为:Thread-0在 1443855101706 进入同步代码块
线程名称为:Thread-0在 1443855104708 离开同步代码块
线程名称为:Thread-1在 1443855104708 进入同步代码块
线程名称为:Thread-1在 1443855107708 离开同步代码块
Этот пример доказывает, что при условии, что несколько потоков содержат «монитор объектов» как один и тот же объект, только один поток может одновременно выполнять код в синхронизированном (не этом объекте x) блоке кода.
Блокировка объектов, отличных от this, имеет определенные преимущества: если в классе много синхронизированных методов, хотя синхронизация в это время может быть достигнута, она будет заблокирована, что повлияет на эффективность. Однако, если блок кода синхронизации блокирует объект, отличный от этого, программа в блоке кода синхронизированного (не этого объекта x) является асинхронной с методом синхронизации и не конкурирует с другими методами блокировки этой блокировки для этой блокировки, которая значительно повышает эффективность бега.
На самом деле и метод, и кодовая блокировка блокируются монитором объектов, блокируемый код синхронизируется, блокировка this — текущий объект, блокировка String — объект String, блокировка Object — объект Object, и они не мешать друг другу. Если есть другие потоки, вызывающие методы или коды, которые используют те же объекты, что и lock this, Object и String выше, им необходимо дождаться синхронизации. Блок кода блокировки более гибок, чем метод блокировки. Поскольку метод блокировки блокирует this, который является текущим объектом, когда поток вызывает метод текущего объекта, другие потоки не могут вызывать код других блокировок этого объекта, а также не могут быть скорректированы все методы блокировки объекта.
Блокировкой является текущий поток. Для этого кода или метода заблокированного объекта одновременно может выполняться только один поток. Если здесь выполняются другие потоки, они будут приостановлены. Если выполняются другие незаблокированные потоки, они являются асинхронными. Запутался в многопоточности. Один поток выполняется синхронно. Когда поток заблокирован, последующий код (внутри блокировки и вне блокировки) ничего не будет выполнять и продолжит падать только тогда, когда он проснется или вернется в нормальное состояние. После обхода кода в замке, замок будет разблокирован, а затем продолжить ходить по оставшимся кодам
Обратите внимание на предложение «private String anyString = new String();», теперь это глобальный объект, поэтому отслеживается тот же объект. Если переместить в try, то монитор объекта будет уже не тот, да и вызов естественно асинхронный, так что можно попробовать самому.
Последнее, что нужно упомянуть, синхронизированный (не этот объект x), если этот объект является переменной экземпляра, он ссылается на ссылку объекта,Пока ссылка на объект не меняется, даже если свойства объекта изменены, результат операции все равно синхронизируется.