#java #multithreading #synchronization
#java #многопоточность #синхронизация
Вопрос:
Я пытался изучить синхронизацию потоков в Java и узнал о синхронизированном блоке. Возможно, я задаю глупый вопрос, поскольку я мало что знаю о синхронизации потоков, но в этой программе я не понимаю поведение блока.
class Table {
void printTable(int n) { //method not synchronized
for(int i=1; i<=10; i ) {
System.out.println(n " * " i " = " n*i);
try {
Thread.sleep(500);
} catch(Exception e) {
System.out.println(e);
}
}
System.out.println("Table of " n " is completed.");
}
}
class MyThread extends Thread {
Table t;
int num;
MyThread(Table t, int num) {
this.t = t;
this.num = num;
}
public void run() {
synchronized(t) {
t.printTable(num);
}
}
}
class TestSynchronization {
public static void main(String[] args) {
Table obj = new Table(); //only one object
MyThread t1;
MyThread t2;
t1 = new MyThread(obj, 10);
t2 = new MyThread(obj, 17);
t1.start();
t2.start();
}
}
Его вывод выглядит следующим образом:
10 * 1 = 10
10 * 2 = 20
10 * 3 = 30
10 * 4 = 40
10 * 5 = 50
Table of 10 is completed.
17 * 1 = 17
17 * 2 = 34
17 * 3 = 51
17 * 4 = 68
17 * 5 = 85
Table of 17 is completed.
Что кажется правильным, но когда я пытаюсь сделать то же самое, удалив синхронизированный блок из метода run и добавив его в obj
объект void main
, он показывает другой результат.
class Table {
void printTable(int n) { //method not synchronized
for(int i=1; i<=5; i ) {
System.out.println(n " * " i " = " n*i);
try {
Thread.sleep(500);
} catch(Exception e) {
System.out.println(e);
}
}
System.out.println("Table of " n " is completed.");
}
}
class MyThread extends Thread {
Table t;
int num;
MyThread(Table t, int num) {
this.t = t;
this.num = num;
}
public void run() {
t.printTable(num);
}
}
class TestSynchronization {
public static void main(String[] args) {
Table obj = new Table(); //only one object
MyThread t1;
MyThread t2;
synchronized(obj) {
t1 = new MyThread(obj, 10);
t2 = new MyThread(obj, 17);
}
t1.start();
t2.start();
}
}
Вывод:
10 * 1 = 10
17 * 1 = 17
10 * 2 = 20
17 * 2 = 34
17 * 3 = 51
10 * 3 = 30
17 * 4 = 68
10 * 4 = 40
10 * 5 = 50
17 * 5 = 85
Table of 17 is completed.
Table of 10 is completed.
Почему это не работает во втором случае, пожалуйста, объясните мне.
Также предложите мне способ, которым я могу получить тот же результат, используя синхронизированный блок, void main
если это возможно.
Комментарии:
1. зачем отклонять оп? вопрос выглядит вполне разумным для меня .. если вы не думаете, что это вопрос домашнего задания, который… хммм
Ответ №1:
Разница заключается в том, где и когда lock
был получен объект класса to the Table.
В вашем первом примере блокировка объекта Table была получена внутри экземпляров MyThread
class . Предположим, что первый экземпляр класса MyThread получает блокировку объекта table, никакие другие экземпляры класса MyThread не смогут получить блокировку объекта table до тех пор, пока первый экземпляр не освободит его, также известный как синхронизация на уровне потока.
Во втором примере блокировка объекта была получена программой-драйвером, поэтому технически на этом уровне нет проблем с параллелизмом, поскольку блокировка привязана к программе-драйверу, а не к отдельным потокам, что на самом деле является своего рода синхронизацией на уровне процесса.
Комментарии:
1. Спасибо @ultrajohn. Теперь я понял свою ошибку. Также понял концепцию. На самом деле я очень новичок в синхронизации Java, поэтому я не мог понять эту концепцию.
2. @Niraj, никаких проблем. Пожалуйста, сделайте мне одолжение и проголосуйте за мой пост. 🙂 Спасибо.
Ответ №2:
Синхронизированный блок — это комната, а синхронизированный блок применяется к объекту. В первом примере синхронизированный блок применяется к объекту таблицы классов. Когда какой-либо поток пытается получить доступ к методу, он должен сначала войти в синхронизированный блок, как человек, входящий в комнату, и когда поток, подобный t1, входит в блок, комната блокируется до тех пор, пока выполнение не будет завершено t1, и только после этого поток t2 может войти в комнату.
Без синхронизированного блока потоки чередуются друг с другом, и, следовательно, выходные данные отличаются, иногда может быть, что вывод корректен, если t1 завершает выполнение непосредственно перед запуском t2, но в случаях большого приложения это будет мешать и приведет к другому выводу, как вы упомянули во втором примере. Поэтому, чтобы предотвратить такое чередование, мы используем синхронизированные блоки или безопасные классы, такие как, блокирующая очередь и т. Д.
Ответ №3:
Синхронизированный блок
synchronized(mutex){
x =y;
y=z;
}
мьютекс = взаимоисключающий.
Блок, который может быть выполнен одним потоком из списка потоков, которые совместно используют один и тот же объект, т.е. мьютекс
Прежде всего, мьютекс — это объект, и каждый объект в java имеет семофор, называемый lock .
если потоки A и B имеют общий мьютекс как c, то поток A перед входом в блок синхронизации должен будет получить блокировку, т.Е. Получение произойдет перед входом
и освобождение происходит до получения, т.Е. Если один поток уже получил блокировку, тогда другой поток должен ждать, пока первый поток не снимет блокировку.
еще одна особенность synchronise block заключается в том, что каждому потоку, получившему блокировку, необходимо прочитать всю переменную, включенную в блок, непосредственно из основной памяти, а не значение остановки из кэша, и записать их, если они изменены, в основную память, прежде чем покинуть блок.
т.е. Обновление значения переменной происходит после получения, а запись в память происходит перед выпуском, поэтому следующий поток будет иметь последнее значение для обработки.