不同的线程,访问相同的同步方法,返回意外结果

时间:2018-09-22 09:20:31

标签: java multithreading synchronized

我很难理解同步如何在来自同一对象的两种不同方法上工作。 我有一个类,其中有两个实例方法被声明为已同步。 三个线程访问该对象的两个同步方法,但结果出乎意料。线程可互换地访问这两种方法。他们不等待整个对象的锁定被释放。 这是示例:

public class ThreadSafeCounterSameMonitor2 {

    private int value;

    public synchronized int getValue() {
        return this.value;
    }

    public synchronized void setValue(int value) {
        this.value = value;
    }

    public static void main(String[] args) {

        ThreadSafeCounterSameMonitor2 nts = new ThreadSafeCounterSameMonitor2();

        Thread th1 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(5);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 5, value=" + nts.getValue());
            }
        });

        Thread th2 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(10);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 10, value="
                        + nts.getValue());
            }
        });

        Thread th3 = new Thread(new Runnable() {
            public void run() {
                nts.setValue(15);
                System.out.println("Thread Id " + Thread.currentThread().getId() + ", expected value is 15, value="
                        + nts.getValue());
            }
        });

        th1.start();
        th2.start();
        th3.start();
    }

}

我得到的结果令人惊讶:

Thread Id 13, expected value is 15, value=15.  
Thread Id 12, expected value is 10, value=15.  
Thread Id 11, expected value is 5, value=15.  

所以,我原本期望10的地方是15。我原本期望5的地方还是15。

我的理解是,监视器中所有同步方法在被线程访问时都会被锁定。但事实并非如此。在线程的同一run()方法中,我设置了一个值,然后检索它,它给了我另一个从另一个线程更改的值。

我经常读到类似“同步方法在此引用上获取隐式锁”之类的内容,但这显然是不正确的,即使对于同一监视器中的所有方法同步方法也是如此。还是我在代码中做得不好?

我的问题是,如何才能真正锁定整个对象的同步方法?您知道一个可以很好地解释这个概念的教程吗?

2 个答案:

答案 0 :(得分:5)

这里没有不可预测的结果。并且同步正常工作。问题是您先调用方法setValue(),然后再调用getValue()。在您的示例线程中,线程以这种方式重叠,即在其他线程调用setValue()之后但在任何线程调用getValue()之前设置值15。

您可以在第三个线程的Thread.sleep(1000)方法中添加run(),以查看结果可能会发生变化。

同步方法获得对对象的显式锁定,但仅在该方法的持续时间上。方法完成后,将返回锁,其他线程可以自由使用它。

答案 1 :(得分:2)

您已经创建了3个线程并开始运行,因此,我希望您知道不能立即保证线程的执行。您创建并启动的线程可能会在末尾开始运行,这种情况正在发生。但是,这并不总是保证。例如,我执行了相同的代码,并在Ideone

上提供了预期的输出
Thread Id 8, expected value is 5, value=5
Thread Id 9, expected value is 10, value=10
Thread Id 10, expected value is 15, value=15

但是,如果您要一个接一个地执行线程,则可以使用join方法。