как работает синхронизированный блок? - PullRequest
0 голосов
/ 08 января 2019

Я учусь многопоточности. Я написал программу с использованием синхронизации для печати таблицы 10 и 5 с использованием двух потоков. В то время как синхронизированный метод дает ожидаемый результат, синхронизированный блок - нет. Что я делаю неправильно?

public class SynchronizationDemo {

public static void main(String[] args) {
    Thread1 t=new Thread1(10);
    Thread1 t1=new Thread1(5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}
//synchronized method
/*public static synchronized void printTable(int num) {
    for (int i = 1; i <= 10; i++) {
        System.out.println(num*i);
        try {
            Thread.sleep(1000);
        }catch(InterruptedException ie){
            ie.printStackTrace();
        }
    }
}*/
//synchronized block
public void printTable(int num)
{
    synchronized(this){
        for (int i = 1; i <= 10; i++) {
            System.out.println(num*i);
            try {
                Thread.sleep(1000);
            }catch(InterruptedException ie){
                ie.printStackTrace();
            }
        }
     }
   }
 }

class Thread1 implements Runnable{
    int num;
    Thread1(int num){
        this.num=num;
    }
    @Override
    public void run() {
        new SynchronizationDemo().printTable(num);
    }
}

Вывод кода: 10 5 10 20 30 15 20 40 25 50 60 30 70 35 40 80 90 45 100 50

Ожидаемый результат: 10 20 30 40 50 60 70 80 90 100 5 10 15 20 25 30 35 40 45 50

Ответы [ 4 ]

0 голосов
/ 08 января 2019

Вы создаете новый экземпляр объекта SynchronizationDemo в методе потока run().

@Override
public void run() {
    new SynchronizationDemo().printTable(num);
}

Таким образом, у каждого потока есть новый объект. Таким образом, в SynchronizationDemo.class синхронизированные блоки бесполезны, если два или более потока не имеют доступа к одному и тому же объекту. Из-за этого потоки работают разные объекты и каждый поток выводит свой номер.

Запустите ваш код 2 или более раз, вы можете увидеть другой вывод, если thread1 работает раньше, чем thread2 или нет.

Вы должны использовать ключевое слово static synchronized с методом printTable;

public static synchronized  void printTable(int num){
     //do sth.
}
0 голосов
/ 08 января 2019

Вы используете ключевое слово synchronized в двух разных контекстах.

  1. static synchronized метод ожидает и получает блокировку на уровне класса SynchronizationDemo, и для этого класса существует только одна блокировка. Таким образом, все экземпляры этого класса должны будут ожидать этой блокировки в последовательности.

  2. synchronized(this) блок (или даже метод) ожидает и получает блокировку объектов класса SynchronizationDemo, и для каждого объекта существует блокировка. Поскольку каждый метод run () создает свой собственный экземпляр new SynchronizationDemo(), этому методу не нужно ждать завершения других методов.

Попробуйте изменить класс Thread1, передав объект.

class Thread1 implements Runnable{
    SynchronizationDemo demo;
    int num;

    Thread1(SynchronizationDemo demo, int num){
        this.demo = demo;
        this.num = num;
    }

    @Override
    public void run() {
        demo.printTable(num);
    }
}

Теперь вы можете передать один и тот же объект класса SynchronizationDemo в оба потока, как показано ниже.

public static void main(String[] args) {
    SynchronizationDemo demo = new SynchronizationDemo();
    Thread1 t=new Thread1(demo, 10);
    Thread1 t1=new Thread1(demo, 5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}
0 голосов
/ 08 января 2019

Основное различие между этими двумя подходами заключается в небольшой, но важной детали.

  • Ваш синхронизированный блок синхронизируется на this.

  • Ваш синхронизированный метод (закомментированный!) - это метод static. Это означает, что он синхронизируется с объектом класса SynchronizationDemo !!

Но это не все объясняет. Другое дело, как вы вызываете метод.

    new SynchronizationDemo().printTable(num);

Вы создаете новый экземпляр и затем вызываете метод для него.

  • Когда вы вызываете статический метод, не имеет значения, какой экземпляр вы используете ... потому что вы синхронизируете объект Class.

  • Когда вы вызываете метод экземпляра, экземпляры различаются, и, следовательно, взаимного исключения не происходит вообще. Вы получаете взаимное исключение и правильную синхронизацию только тогда, когда два потока синхронизируются на одном и том же объекте.

0 голосов
/ 08 января 2019

Разница в том, что вы также изменили метод, чтобы он больше не был static.

public static synchronized void printTable(int num) { }

Это блокирует класс, а не отдельный экземпляр .

Эквивалентный синхронизированный блок будет

synchronized(SynchronizationDemo.class){
}

Таким образом, даже если у вас есть два экземпляра, они по-прежнему используют одну и ту же блокировку (поскольку они оба принадлежат к одному и тому же классу), тогда как synchronized(this) блокирует два независимых объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...