синхронизированное ключевое слово в потоках Java

#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();
}
  

означает следующее:

  1. Если object.owner == Thread.currentThread() , тогда просто продолжайте и увеличивайте счетчик.
  2. Если object.owner == null , затем запустите object.owner = Thread.currentThread() , установите этот счетчик равным 1 и продолжайте.
  3. В противном случае (итак, object.owner — это какой-то другой поток), остановитесь, заморозьте поток и подождите, пока для владельца не будет установлено значение null, а затем мы можем перейти к варианту # 2 вместо этого.
  4. Как только мы войдем, запустите code() . Когда мы дойдем до закрывающей фигурной скобки, уменьшите счетчик. Если оно равно 0, запустите object.owner = null .
  5. Кроме того, все вышеперечисленное выполняется атомарно — 2 потока не могут попасть в состояние гонки, выполняя все это. Например, если 2 потока ожидают, что владелец снова отключится, только один «получит его», а другой продолжит ждать. (Какой из них его получает? Виртуальная машина может выбирать все, что захочет; вы должны предположить, что это произвольно, но несправедливо. Другими словами, не пишите код, который зависит от определенного выбора).
  6. Метод, который имеет ключевое synchronized слово, — это просто синтаксический сахар для обертывания ВСЕГО кода внутри него в synchronized(this) методах for instance и synchronized(MyClass.this) для статических методов.

Обратите внимание, что synchronized поэтому взаимодействует только с другими synchronized блоками и только с теми блоками, для которых объект в круглых скобках является точно такой же ссылкой на obj, в противном случае ничего из этого не делает. Это, конечно, не запускает потоки! Все, что делает synchronized, потенциально приостанавливает потоки.

В вашем коде вы поместили ВЕСЬ код запуска в один гигантский синхронизированный блок, синхронизируясь с экземпляром вашего потока. Как правило, когда вы синхронизируете что-либо, это общедоступный API — другой код может синхронизироваться с тем же и влиять на вас. Точно так же, как мы обычно не пишем общедоступные поля в java, вы не должны блокировать общедоступные вещи и this обычно являются общедоступными (например, код, который вы не контролируете, может содержать ссылку на вас). Так что не делайте этого, если вы не хотите указать в своих документах, как настроено ваше поведение блокировки. Вместо этого создайте внутреннее частное конечное поле, назовите его lock и используйте это ( private final Object lock = new Object(); ) .