一个对象的两个同步方法是否可能同时被两个线程访问?

4

这个问题是在我的面试中问到的。在我回答之前,

一旦一个线程进入实例上的任何同步方法,其他线程就不能进入该实例上的任何其他同步方法。

考虑下面的代码片段:

Q1:

public class Q1 {
    int n;
    boolean valueSet = false;

    synchronized int get() {
        while (!valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        System.out.println("Got: " + n);
        valueSet = false;
        notify();
        return n;
    }

    synchronized void put(int n) {
        while (valueSet)
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println("InterruptedException caught");
            }
        this.n = n;
        valueSet = true;
        System.out.println("Put: " + n);
        notify();
    }
}

制作人1:

public class Producer1 implements Runnable {

    Q1 q;

    Producer1(Q1 q) {
        this.q = q;
        new Thread(this, "Producer").start();
    }

    @Override
    public void run() {

        int i = 0;
        while (true) {
            q.put(i++);
        }

    }
}

Consumer1

public class Consumer1 implements Runnable {

    Q1 q;

    Consumer1(Q1 q) {
        this.q = q;
        new Thread(this, "Consumer").start();
    }

    @Override
    public void run() {
        while (true) {
            q.get();
        }

    }

}

PC1:

public class PC1 {

    public static void main(String args[]) {

        Q1 q = new Q1();
        new Producer1(q);
        new Consumer1(q);
        System.out.println("Press Control-C to stop.");

    }
}

因此,他问道,一旦您创建了此线程 new Producer1(q),那么根据您的说法,当它访问synchronized int put()时,synchronized int get()方法必须由相同的线程即new Producer1(q)锁定。我回答说是的。

但是我在eclipse中检查发现,new Consumer1(q)可以调用get。程序运行良好。

我错在哪里了?

输出:

enter image description here


1
wait() 函数是做什么用的? - chrylis -cautiouslyoptimistic-
@chrylis: 我的理解是,wait( ) 告诉调用线程放弃监视器并进入休眠状态,直到其他线程进入同一监视器并调用 notify( )。 - Farhan stands with Palestine
@chrylis的wait()正在等待……另一个线程被通知。 - Bahramdun Adil
Bahramdun:当它被新的Producer1线程锁定时,如何在已同步的方法内部调用wait? - Farhan stands with Palestine
wait()方法使线程进入休眠状态,直到其他线程通知该线程。 - Bahramdun Adil
简短回答:每个线程都可以在另一个线程等待它这样做时调用wait - David Schwartz
6个回答

6
调用 wait() 方法将在等待时间内释放监视器。
这就是 Object.wait() 文档中所记录的:
当前线程必须拥有该对象的监视器。该线程释放该监视器的所有权并等待,直到另一个线程通过调用 notify 方法或 notifyAll 方法来唤醒正在等待该对象监视器的线程。然后该线程等待重新获取监视器的所有权并继续执行。

2
不,这不是答案。你还没有真正理解问题。 - Farhan stands with Palestine
5
@Shirgill:显然这里没有人理解你的问题。 - Nathan Hughes

1

一旦线程进入实例上的任何同步方法,其他线程就不能进入该实例上的任何其他同步方法。

你忘了补充的是"除非锁被释放"

...这在调用wait时是成立的。

文档中指定:

线程释放此监视器的所有权并等待,直到另一个线程通过调用notify方法或notifyAll方法通知等待此对象监视器上的线程唤醒。

由于锁被释放,在另一个方法中进行(并且条件为真,因为布尔值已修改)。进入另一个方法后,您再次释放锁,然后调用notify并唤醒旧线程,旧线程终止(重新修改布尔值以通过其他方法中的条件,并通知)。这样,您可以无限地在两个方法之间循环。


@ShirgillFarhanAnsari 当线程在wait()内部时,就好像它不在synchronized内部一样。 - alain
@alain:但你不觉得在进入你提到的等待之前,线程必须先遇到该方法的同步关键字吗? - Farhan stands with Palestine
@ShirgillFarhanAnsari 是的,但是等待会按照我回答末尾的引用释放它。 - Jean-François Savard

0

wait()notify() 作为线程之间的信号,用于控制线程执行或不执行操作。


0
程序运行完美,因为这里有两个线程(生产者、消费者)争夺一个锁(监视器)。当消费者获取锁(Q1对象)时,生产者正在等待锁。当消费者完成工作后,它释放锁。当调用wait()方法时,消费者也会释放锁,因为wait()方法将线程设置为等待状态并释放锁。现在轮到生产者获取锁并执行它的工作了。当生产者线程调用notify()方法时,消费者继续工作(当获取锁时)。对于生产者也是一样的。
总结:Q1对象是所有线程的锁。如果有人获取了它,其他人就会被阻塞,答案是 - 不可能同时访问get()、put()方法超过2个线程。

0

我认为这个问题不够明确,例如,“可访问”是什么意思?

在我看来,一个好的面试问题不应该只有正确或错误的答案。一个好的面试问题应该是一个谈话的开端,让您有机会展示您对主题的了解程度。

当我提问面试问题时,我喜欢那些能够看透问题并深入了解其基本机制的候选人。例如,

What the JLS guarantees is that no two threads can be _synchronized_
on the same instance at the same time...

接下来我们可以探讨这样的问题,两个线程如何同时进入同一个synchronized方法呢?(例如在不同实例上同步),两个线程如何同时在同一实例的同步方法中(其中一个可能在等待(wait()))...


-3

除非线程已经在守卫该块的对象上获得了锁,否则线程无法访问同步代码块。在您的情况下,synchronized关键字使用声明它的对象的锁。因此,在一个线程执行get()时,没有其他线程可以执行put()

如果您应用这个方法,当put()设置值时,它会通知接受该值的消费者。即使您从get和put方法中删除了wait()和notify()调用,代码也应该能够正常工作。


不会的,如果你想试一下也可以。 - Jean-François Savard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接