#java #multithreading #synchronization
#java #многопоточность #синхронизация
Вопрос:
Я пытаюсь понять, как работает многопоточность (особенно с синхронизированным ключевым словом).В этом примере я хочу, чтобы второй поток abc1 начал выполняться после потока abc. Итак, я использовал ключевое слово synchronized в функции run.Но строка вывода, в которой говорится:
Начальный баланс в этом потоке равен 10000
Начальный баланс в этом потоке равен 10000
это то, что меня беспокоит.Поскольку начальный баланс должен быть «-243000», как указано в строке вывода
Конечный баланс после начального -243000 составляет 59049000
потому что поток abc1 должен ждать abc из-за синхронизированного ключевого слова. Прежде всего, я хочу, чтобы потоки вели себя так, как будто я пишу
abc.start
abc.join()
abc1.start()
abc1.join()
Вот мой исходный код:
class parallel extends Thread{
account a;
public parallel(account a) {
this.a=a;
}
public synchronized void run() {
synchronized(this) {
System.out.println("Initial balance in this thread is " a.amount);
long duplicate=a.amount;
boolean flag=true;
//System.out.println("Transaction inititated");
for(int i=0;i<10;i ) {
if(flag==true) {
//System.out.println("Deducting " amount "Rs from your account");
a.amount-=a.amount*2;
}
else {
//System.out.println("Depositing " amount "Rs from your account");
a.amount =a.amount*2;
}
flag=!flag;
}
System.out.println("Final balance after intial " duplicate " is " a.amount);
syncro.amount=a.amount;
}
}
}
class account{
public account(long rupe) {
amount=rupe;
}
long amount;
}
public class syncro {
static long amount;
public static void main(String[] args) throws InterruptedException{
//for(int i=0;i<10;i ) {
account ramesh=new account(1000);
parallel abc=new parallel(ramesh);
parallel abc1=new parallel(ramesh);
abc.start();
//abc.join();
abc1.start();
//abc1.join();
//}
//awaitTermination();
//Thread.sleep(4000);
boolean ab=true;
long cd=1000;
for(int i=0;i<10;i ) {
if(ab==true) {
//System.out.println("Deducting " ab "Rs from your account");
cd-=cd*2;
}
else {
//System.out.println("Depositing " a "Rs from your account");
cd =cd*2;
}
ab=!ab;
}
//System.out.println("Final amount by multithreading is " );
System.out.println("Final amount after serial order is " cd);
}
}
Ответ №1:
Вы смешиваете создание собственных потоков с использованием synchronized . Кроме того, использование synchronized (this) в синхронизированном методе выполняет одно и то же дважды.
Synchronized — это НЕ запуск потоков. Речь идет о том, чтобы разрешить только одному потоку вводить определенный блок кода одновременно.
Комментарии:
1. Итак, на самом деле выполняются два разных потока, поэтому ключевое слово synchronized не имеет значения, поскольку они выполняют свои собственные методы запуска. Я прав?
2. Да, вы правы. Synchronized позволяет только одному потоку вводить блок кода (для каждого экземпляра для методов экземпляра и для каждого класса для статических методов). И у вас на самом деле есть 2 экземпляра.
3. кстати: синхронизация метода запуска потока в значительной степени разрушает концепцию потока, и ее следует избегать — даже просто для примера.
Ответ №2:
У каждого создаваемого вами объекта есть скрытое поле, которое вы не можете прочитать, но оно существует. Оно имеет тип Thread и вызывается owner
.
synchronized
Ключевое слово взаимодействует с этим скрытым полем.
synchronized (object) {
code();
}
означает следующее:
- Если
object.owner == Thread.currentThread()
, тогда просто продолжайте и увеличивайте счетчик. - Если
object.owner == null
, затем запуститеobject.owner = Thread.currentThread()
, установите этот счетчик равным 1 и продолжайте. - В противном случае (итак, object.owner — это какой-то другой поток), остановитесь, заморозьте поток и подождите, пока для владельца не будет установлено значение null, а затем мы можем перейти к варианту # 2 вместо этого.
- Как только мы войдем, запустите code() . Когда мы дойдем до закрывающей фигурной скобки, уменьшите счетчик. Если оно равно 0, запустите
object.owner = null
. - Кроме того, все вышеперечисленное выполняется атомарно — 2 потока не могут попасть в состояние гонки, выполняя все это. Например, если 2 потока ожидают, что владелец снова отключится, только один «получит его», а другой продолжит ждать. (Какой из них его получает? Виртуальная машина может выбирать все, что захочет; вы должны предположить, что это произвольно, но несправедливо. Другими словами, не пишите код, который зависит от определенного выбора).
- Метод, который имеет ключевое
synchronized
слово, — это просто синтаксический сахар для обертывания ВСЕГО кода внутри него вsynchronized(this)
методах for instance иsynchronized(MyClass.this)
для статических методов.
Обратите внимание, что synchronized
поэтому взаимодействует только с другими synchronized
блоками и только с теми блоками, для которых объект в круглых скобках является точно такой же ссылкой на obj, в противном случае ничего из этого не делает. Это, конечно, не запускает потоки! Все, что делает synchronized, потенциально приостанавливает потоки.
В вашем коде вы поместили ВЕСЬ код запуска в один гигантский синхронизированный блок, синхронизируясь с экземпляром вашего потока. Как правило, когда вы синхронизируете что-либо, это общедоступный API — другой код может синхронизироваться с тем же и влиять на вас. Точно так же, как мы обычно не пишем общедоступные поля в java, вы не должны блокировать общедоступные вещи и this
обычно являются общедоступными (например, код, который вы не контролируете, может содержать ссылку на вас). Так что не делайте этого, если вы не хотите указать в своих документах, как настроено ваше поведение блокировки. Вместо этого создайте внутреннее частное конечное поле, назовите его lock и используйте это ( private final Object lock = new Object();
) .