【问题标题】:Locking instances with synchronized block inside non-static method在非静态方法中使用同步块锁定实例
【发布时间】:2019-03-02 20:08:30
【问题描述】:

按照下面的代码,我有两个 A 类实例 - a1 和 a2。并分别在两个实例上调用方法 foo()。

foo() 方法中有一个同步块,它被锁定在调用对象上。由于它是实例级锁定,因此这两个方法应该同时开始执行,因为它们是从两个单独的实例中调用的。但是,它们是按顺序执行的。

是不是因为两个实例都是从同一个主线程调用的?

代码更改:使 A 类实现 Runnable,将 foo() 重命名为 run(),从 main 分叉一个线程 t,从主线程调用 a1.run(),从线程 t 调用 a2.run()。尽管从两个线程(主线程和线程 t)调用了两个 A 实例 - a1 和 a2,但同步块(this)似乎被锁定了。

我的理解是“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) {}
        }
    }

}

【问题讨论】:

    标签: multithreading synchronization thread-safety synchronized thread-synchronization


    【解决方案1】:

    你在启动线程之前就开始同步运行a1,当然你会在主线程上得到a1的输出,因为在a1完成之前它无法到达线程启动语句。

    尝试先启动运行a2 的线程,然后再在主线程上运行a1,看看会得到什么。

    您还应该注意线程调度可能会延迟,并且调用Thread#start 不会立即开始在单独的线程上执行,而是将其排队等待系统线程调度程序。您可能还需要考虑使用诸如CyclicBarrier 之类的同步设备,以便在运行a2 的线程和运行a1 的主线程之间进行协调,否则您可能仍然得到准确的即使您似乎在a2 之前启动线程以运行a1,也会得到相同的结果。

    【讨论】:

    • 在 a1.run() 解决问题之前添加 t.start()。我现在得到了预期的输出。但是,没有得到使用 CyclicBarrier 线程之间同步的概念。会调查的。
    【解决方案2】:

    是不是因为两个实例都从同一个调用 主线程?

    是的。调用 Thread.sleep() 是同步的,除非被中断,否则将在持续时间内阻塞当前线程。您直接调用 a1.foo() 这将在持续时间内阻塞主线程,这就是您看到的结果。创建单独的线程并在每个线程中调用 foo() ,您将看到您所期望的行为。

    【讨论】:

    • 已对代码进行了编辑,以从两个单独的线程 - main 和 t 调用两个实例 a1 和 a2 的运行方法。但结果还是一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多