Java синхронизация и потоки - PullRequest
1 голос
/ 01 августа 2020
import java.util.Random;

public class ClassA{
  int a = 0, b = 10;
  Random rand = new Random();

  public synchronized void add(){
      if (a < 10){ 
         a++;
         b--;
      };
  }

  public synchronized void subtract(){
      if (b < 10){ 
         b++;
         a--;
      }
  }
  
  public synchronized boolean check(){
     return a + b == 10;
  }
  
  
  public static void main(String args[]){
      
      ClassA a = new ClassA();
      
      B ba = new B(a);
      B bb = new B(a);
      
       //notice these two threads are not associated with the object a!
      Thread t11 = new Thread(bb);
      Thread t22 = new Thread(ba);
      t11.start(); 
      t22.start();
  }
}


class B implements Runnable{
    ClassA a;
    
    B(ClassA a){
        this.a = a;
    }
   
    @Override
    public void run(){
        while (a.check()){
          if (a.rand.nextInt(2) == 1)
              a.subtract();
          else
              a.add();
      } System.out.print("Out of sync");
    }
}

Пытаюсь лучше понять java многопоточность. Итак, я знаю, что, как говорится, только один поток может получить доступ к экземпляру объекта и, таким образом, вызвать метод этого экземпляра (в данном случае, например, метод subtract в классе classA.

Но что происходит, когда , у вас все еще есть тот же экземпляр classA, a, но два потока, связанных с другим классом classB, все вместе пытаются вызвать методы a? Я ожидал, что сообщение out of sync никогда не будет напечатано В конце концов, остался только один экземпляр объекта a, означает ли это, что синхронизация не применяется в этом примере?

1 Ответ

1 голос
/ 01 августа 2020

В вашем случае out of sync никогда не будет напечатан, потому что для вашей ситуации многопоточность реализована правильно: критические методы, которые выполняют последовательность операций и, следовательно, не являются atomi c, ведут себя как atomi c методы с использованием ключевого слова synchronized в методе.

Предположим, что объявлен блок кода synchronized (x). Первый поток, который входит в этот блок кода, получает блокировку объекта x и успешно выполняет этот блок кода.

Любой не первый поток, который пытается войти в блок на том же объекте x, "приостановлен" "путем помещения потока в пул блокировок из x. Как только поток, который в настоящее время имеет блокировку, покидает блок synchronized (x), случайный поток из пула блокировок становится следующим потоком, выполняющим этот блок.

При использовании synchronized в методе экземпляра он фактически то же самое, что обертывание всего содержимого метода с помощью synchronized (this). (Для методов static это будет объект класса включающего класса.)

Итак, в вашем случае есть один экземпляр класса A, и вся синхронизация происходит с ним.

Если вы хотите увидеть сообщение out of sync, попробуйте удалить ключевое слово synchronized. Затем через некоторое время вы должны увидеть ожидаемое сообщение out of sync.

Примечания:

  • Поскольку out of sync является сообщением, его, вероятно, следует напечатать на System.err, а не System.out.
  • Вместо a.rand.nextInt(2) == 1 вы можете использовать a.rand.nextBoolean().
  • Имея class B поле доступа rand из class A, инкапсуляция нарушается, вы можете захотеть дать class A метод randomBoolean(), который выполняет return rand.nextBoolean(), и вызывает этот метод randomBoolean() из class B.
  • Наличие одинаковых имен переменных с разными значениями в разных контекстах может сбивать с толку. В class A переменные a и b обозначают два числа, в других контекстах переменные a и b относятся к экземплярам class A и class B. Попробуйте переименовать поля class A во что-нибудь другое, например number1 и number2.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...