【问题标题】:Do two synchronized methods execute simultaneously两个同步方法是否同时执行
【发布时间】:2011-03-10 18:08:45
【问题描述】:

我在一个类中有 4 种方法(m1m2m3m4)。方法m1m2m3synchronized 方法。另外,我分别有 4 个线程 t1t2t3t4

如果t1访问m1方法(同步方法),t2线程可以同时访问m2方法(同步方法)吗?如果不是,t2的状态是什么?

【问题讨论】:

    标签: java multithreading synchronized


    【解决方案1】:

    如果t1访问m1方法(同步方法),t2线程可以同时访问m2方法(同步方法)吗?

    synchronized 关键字适用于对象级别,并且只有一个线程可以持有对象的锁。所以只要你说的是同一个对象,那么t2会等待t1释放它进入m1时获得的锁。

    但是,线程可以通过调用Object.wait() 来释放锁而无需从方法返回。

    如果不是,t2 的状态是什么?

    它会紧紧地等待t1 释放锁(从方法返回或调用Object.wait())。具体来说,它将位于BLOCKED state

    一个线程阻塞等待监视器锁的线程状态。处于阻塞状态的线程正在等待监视器锁进入同步块/方法或调用Object.wait后重新进入同步块/方法。

    示例代码:

    public class Test {
    
        public synchronized void m1() {
            try { Thread.sleep(2000); }
            catch (InterruptedException ie) {}
        }
    
        public synchronized void m2() {
            try { Thread.sleep(2000); }
            catch (InterruptedException ie) {}
        }
    
        public static void main(String[] args) throws InterruptedException {
            final Test t = new Test();
            Thread t1 = new Thread() { public void run() { t.m1(); } };
            Thread t2 = new Thread() { public void run() { t.m2(); } };
    
            t1.start();
            Thread.sleep(500);
    
            t2.start();
            Thread.sleep(500);
    
            System.out.println(t2.getState());
        }
    }
    

    输出:

    BLOCKED
    

    【讨论】:

    • 但是如果我们做这样的事情,那会怎样?最终测试 tt1 = new Test();最终测试 tt2 = new Test();线程 t1 = new Thread() { public void run() { tt1.m1(); } };线程 t2 = new Thread() { public void run() { tt2.m2(); } };
    • @aioobe 在同步代码块(不是方法)的情况下会发生什么。两个线程可以同时访问同一个对象的两个不同的同步代码块吗?
    • 取决于您同步的内容on。如果您执行 synchronized(this),则应用完全相同的规则。
    【解决方案2】:

    如果方法在同一个监视器上同步,那么它们不能在不同的线程中同时执行。当第二个线程到达监视器入口(本例中为同步方法的开始)时,它将阻塞,直到第一个线程释放监视器。

    在这种情况下阻塞线程的实际状态,由 jconsole 报告,类似于java.lang.Thread.State: WAITING (on object monitor)

    假设所有方法都是普通的实例方法,那么它们将共享同一个监视器在同一个对象上调用时。也就是说,如果你有类似的东西:

    // Thread 1
    A a1 = new A();
    a1.m1();
    
    // Thread 2
    A a2 = new A();
    a2.m2()
    

    那么在这种情况下,第二个线程将能够调用该方法,因为它试图获取 a2 对象的隐式监视器,该对象没有被线程 1 锁定。但是如果线程 2 尝试调用 a1.m2(),那么它将阻塞,直到线程 1 完成执行 m1()

    如果您有静态方法,那么它们会获得类本身的显式监视器(在我的假设命名情况下为A.class),因此不会被任何实例方法调用阻塞。

    【讨论】:

    • 其实它会处于BLOCKED状态,而不是WAITING状态。
    • 在上面的例子中调用a2.m2() 有点误导。因为a1a2A 类的不同实例,所以每个都有自己的lock。因此,即使a1.m1()a2.m1()也可以同时执行。
    • @derenio - 这就是我在该部分中提出的全部观点,基于之前的粗体声明。由于问题是询问是否可以同时调用 m1m2,这就是我在示例中坚持的内容。
    【解决方案3】:

    不,它不能。这是synchronized 唯一的一点:不同的线程不能同时做这些事情(你不必防止同一个线程同时做这些事情,因为一个线程根本不能并行做任何事情。 ) 等待线程的状态是'waiting for lock'。 (使用足够现代的 JVM,如果您以正确的方式询问,您实际上可以在控制台上显示此状态。)

    【讨论】:

      【解决方案4】:

      如果t1访问m1方法(同步方法),t2线程可以同时访问m2方法(同步方法)吗?

      没有。线程 t2 将等待线程 t1 释放锁。 在您的同一个示例中,t2 可以访问未同步的方法 m4。

      锁定synchronized 方法

      每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行独占和一致访问的线程必须在访问对象之前获取对象的内在锁,然后在完成访问时释放内在锁

      当线程调用同步方法时,它会自动获取该方法对象的内在锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁释放

      回到您的第二个查询:

      如果不是,t2 的状态是什么?

      线程 t2 处于阻塞状态,等待线程 t1 释放锁。

      来自javadocumentation页面:

      使synchronized方法有两个作用。

      首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象。

      其次,当一个同步方法退出时,它会自动与任何后续对同一对象的同步方法调用建立起之前的关系。这保证了对象状态的变化对所有线程都是可见的

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-19
        • 1970-01-01
        • 2019-02-27
        • 2012-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-24
        相关资源
        最近更新 更多