3

我正在尝试使用另一种方法中的同步语句运行一种方法。

我的课

public class MyClass {

    private Object lockObject = new Object();

    public void testFirst() {
        synchronized (lockObject) {
            System.out.println("Entered testFirst() sync");
            testSecond();
            System.out.println("Leaving testFirst() sync");
        }
    }

    private void testSecond() {
        synchronized (lockObject) {
            System.out.println("Entered testSecond() sync");
        }
    }

}

主要的

package com.replanet;

public class Main {

    public static void main(String[] args) {

        MyClass myClass = new MyClass();
        myClass.testFirst();
    }

}

输出

Entered testFirst() sync
Entered testSecond() sync
Leaving testFirst() sync

我期待另一个输出。

Entered testFirst() sync
Leaving testFirst() sync
Entered testSecond() sync

为什么不testSecond()等到testFirst()完成它的工作?

4

6 回答 6

4

您会看到这种看似奇怪的行为,因为每个锁都与请求它的线程相关联,一旦您拥有锁,您就会一直保留它直到您解锁。

在您的代码中,您两次获得相同的锁,因此第二次尝试立即成功,因为您已经拥有它。

请参阅此处以供参考:

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

于 2013-02-25T11:27:42.880 回答
3

由于要进入第一synchronized部分,您必须拥有 object 锁lockObject,当您到达第二synchronized部分时,您仍然拥有它,因此它会继续。运行testFirst时,synchronized块中的块testSecond没有任何变化。

于 2013-02-25T11:28:00.727 回答
2

要记住几件事:

  • synchronizedsynchronized块是可重入的:当您通过语句持有锁时,您可以使用同一个监视器进入另一个块 - 在您的情况下synchronized,没有什么可以阻止您调用testSecondtestFirst
  • 现在想象这两种方法使用不同的监视器,你仍然会得到相同的输出,因为没有其他线程会持有testSecond
  • 如果这 2 个方法使用了两个不同的锁,并且另一个线程持有在testSecond执行中使用的锁,则testFirst不会跳过调用testSecond并继续前进,它将阻塞直到锁再次可用
于 2013-02-25T11:55:39.690 回答
1

因为testSecond()testFirst()相关联

控制流程完全如下:-

System.out.println("Entered testFirst() sync");
       System.out.println("Entered testSecond() sync");
System.out.println("Leaving testFirst() sync");
于 2013-02-25T11:30:54.720 回答
1

同步是围绕称为内在锁或监视器锁的内部实体构建的。
每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行排他和一致访问的线程必须在访问对象之前获取对象的内在锁,然后在完成访问时释放内在锁。在获得锁和释放锁之间,线程被称为拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在尝试获取锁时会阻塞。

因此您的线程是 lockObject 的所有者,因此它可以使用 lockObject 进入块内的所有代码

于 2013-02-25T11:37:53.783 回答
0

我编辑了我发布的代码。同步语句是有道理的,不是吗?

我的课

public class MyClass {

    private Object lockObject = new Object();

    public void testFirst() throws InterruptedException {
        // Here is something that should not be synchronized 
        synchronized (lockObject) {
            System.out.println("testFirst() is about to fall asleep. "
                    + Thread.currentThread().getName());
            Thread.sleep(2000);
            System.out.println("testFirst() woke up. " + Thread.currentThread().getName());
        }
    }

}

我的线程

public class MyThread extends Thread {

    private MyClass myClass;

    public MyThread(MyClass myClass) {
        this.myClass = myClass;
    }

    @Override
    public void run() {
        try {
            myClass.testFirst();
        } catch (InterruptedException e) {
            interrupt();
        }
        super.run();
    }

}

用法

package com.replanet;

public class Main {

    public static void main(String[] args) {
        MyClass myClass = new MyClass();
        MyThread mThread = new MyThread(myClass);
        MyThread anotherThread = new MyThread(myClass);
        mThread.start();
        anotherThread.start();
    }

}

输出

testFirst() is about to fall asleep. Thread-0
testFirst() woke up. Thread-0
testFirst() is about to fall asleep. Thread-1
testFirst() woke up. Thread-1
于 2013-02-25T14:16:14.543 回答