Блокировка экземпляров синхронизированным блоком внутри нестатического метода - PullRequest
0 голосов
/ 27 сентября 2018

Исходя из приведенного ниже кода, у меня есть два экземпляра класса A - a1 и a2.И вызов метода foo () для обоих экземпляров отдельно.

В методе foo () есть синхронизированный блок, который заблокирован для вызывающего объекта.Поскольку это блокировка на уровне экземпляра, оба метода должны начать выполняться одновременно, поскольку они вызываются из двух отдельных экземпляров.Но они выполняются последовательно.

Это потому, что оба экземпляра вызываются из одного и того же основного потока?

Изменения в коде: В класс A реализован Runnable, переименован в foo () в run (), ветвь потока t изmain, вызвал a1.run () из основного потока, вызвал a2.run () из потока t.Хотя два экземпляра A - a1 и a2 вызываются из двух потоков - main & thread t, синхронизированный блок (это), похоже, блокируется.

Насколько я понимаю, слово "this" относится квызов экземпляра Runnable, который отличается, и даже потоки отличаются.Таким образом, Thread.sleep не должен блокировать другой поток.Тогда, почему два вызова run не происходят параллельно?

Ожидаемый вывод (должен выполняться параллельно)

main <time> Inside A.run
Thread-0 <time> Inside A.run
Thread-0 <time+4s> Exiting A.run
main <time+5s> Exiting A.run

Фактический вывод (выполняется последовательно)

main <time> Inside A.run
main <time+5s> Exiting A.run
Thread-0 <time+5s> Inside A.run
Thread-0 <time+9s> Exiting A.run 

import java.time.*;
import java.time.format.DateTimeFormatter;

public class Test {

    public static void main(String[] args) {
        /*A a1 = new A(5000); A a2 = new A(4000);
        a1.foo(); a2.foo();*/
        A a1 = new A(5000); A a2 = new A(4000);
        Thread t = new Thread(a2);
        /*a1.run(); t.start();*/
        t.start(); a1.run(); // <-- putting t.start() before a1.run() solves the issue
    }

}

class A implements Runnable {
    public long waitTime;
    public A() {}
    public A(long timeInMs) {
        waitTime = timeInMs;
    }
    public void run() {
        synchronized(this) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
                LocalDateTime time = LocalDateTime.now();
                System.out.println(Thread.currentThread().getName() + " " + formatter.format(time) + " Inside A.run");
                Thread.sleep(waitTime);
                time = LocalDateTime.now();
                System.out.println(Thread.currentThread().getName() + " " + formatter.format(time) + " Exiting A.run");
            } catch (InterruptedException e) {}
        }
    }

}

Ответы [ 2 ]

0 голосов
/ 27 сентября 2018

Вы начали синхронно запускать a1 перед запуском потока, конечно, вы получите вывод a1 в основном потоке, потому что он не сможет достичь оператора запуска потока, пока не завершится a1.

Попробуйте сначала запустить поток a2, прежде чем запускать a1 в основном потоке, посмотрите, что у вас получится.

Вы также должны знать, что планирование потоков может быть отложено, и вызовите Thread#startне сразу начинает выполнение в отдельном потоке, а ставит его в очередь для системного планировщика потока.Вы также можете рассмотреть возможность использования устройства синхронизации, такого как CyclicBarrier, для координации между потоком, выполняющим a2, и основным потоком, выполняющим a1, в противном случае вы можете все еще получить точно такой жерезультат, даже если кажется, что вы запускаете поток для запуска a1 до a2.

0 голосов
/ 27 сентября 2018

Это потому, что оба экземпляра вызываются из одного и того же основного потока?

Да.Вызов Thread.sleep () является синхронным и блокирует текущий поток на время, если оно не прервано.Вы напрямую вызываете a1.foo (), который на время блокирует основной поток, что является результатом, который вы видите.Создайте отдельные потоки и вызовите foo () в каждом из них, и вы увидите ожидаемое поведение.

...