线程同步 - 线程何时释放对象的锁定

时间:2014-10-07 13:52:16

标签: java multithreading thread-synchronization

public class MyStack2 {
    private int[] values = new int[10];
    private int index = 0;

    public synchronized void push(int x) {
        if (index <= 9) {
            values[index] = x;
            Thread.yield();
            index++;
        }
    }

    public synchronized int pop() {
        if (index > 0) {
            index--;
            return values[index];
        } else {
            return -1;
        }
    }

    public synchronized String toString() {
        String reply = "";
        for (int i = 0; i < values.length; i++) {
            reply += values[i] + " ";
        }
        return reply;
    }
}

public class Pusher extends Thread {
    private MyStack2 stack;

    public Pusher(MyStack2 stack) {
        this.stack = stack;
    }

    public void run() {
        for (int i = 1; i <= 5; i++) {
            stack.push(i);
        }
    }
}

public class Test {
    public static void main(String args[]) {
        MyStack2 stack = new MyStack2();
        Pusher one = new Pusher(stack);
        Pusher two = new Pusher(stack);
        one.start();
        two.start();
        try {
            one.join();
            two.join();
        } catch (InterruptedException e) {
        }
        System.out.println(stack.toString());
    }
}

由于MyStack2类的方法是同步的,我期待输出为 1 2 3 4 5 1 2 3 4 5.但输出是不确定的。通常它给出:1 1 2 2 3 3 4 4 5 5

根据我的理解,当线程1启动时,它会获取push方法的锁定。在push()内部线程中有一段时间会产生。但是在调用yield()时会释放锁吗?现在当线程2启动时,线程2会在线程一完成执行之前获取一个锁吗?有人可以解释一个线程一次释放堆栈对象上的锁吗?

3 个答案:

答案 0 :(得分:5)

synchronized方法只会阻止其他线程在执行时执行它。一旦它返回,其他线程就可以(并且通常会立即)获得访问权。

获取1 1 2 2 ...的方案可能是:

  1. 主题1调用push(1)并允许进入。
  2. 线程2调用push(1)并在线程1使用时被阻止。
  3. 主题1退出push(1)
  4. 线程2获得对push的访问权限并推送1,但同时线程1调用push(2)
  5. 结果1 1 2 - 您可以清楚地看到它是如何继续的。

答案 1 :(得分:2)

当你说:

  

根据我的理解,当线程1启动时,它会获取push方法的锁定。

这不太正确,因为锁不仅仅是推送方法。 push方法使用的锁是在调用push的MyStack2实例上。方法pop和toString使用与push相同的锁。当一个线程在一个对象上调用任何这些方法时,它必须等到它可以获得锁定。调用push的中间线程将阻止另一个线程调用pop。线程调用不同的方法来访问相同的数据结构,对访问该结构的所有方法使用相同的锁,防止线程同时访问数据结构。

一旦线程在退出同步方法时放弃锁定,调度程序就会决定哪个线程接下来获得锁定。你的线程正在获取锁并让它们多次运行,每次释放锁时,调度程序都会做出决定。你不能做出任何关于哪些将被选中的假设,它可以是任何一个。多线程的输出通常是混乱的。

答案 2 :(得分:1)

看起来你可能对synchronized和yield关键字的确切含义感到困惑。

同步意味着一次只能有一个线程进入该代码块。想象一下它作为一扇门,你需要一把钥匙才能通过。进入的每个线程都使用唯一的密钥,并在完成后返回它。这允许下一个线程获取密钥并在其中执行代码。它们在同步方法中的长度并不重要,一次只能输入一个线程。

Yield给编译器建议(并且是唯一的建议)当前线程可以放弃其分配的时间而另一个线程可以开始执行。然而,并非总是这样。

在你的代码中,即使当前线程向编译器建议它可以放弃它的执行时间,它仍然保持同步方法的键,因此新线程无法进入。

不可预知的行为来自于收益而不是像你预测的那样放弃执行时间。

希望有所帮助!