簡體   English   中英

線程同步 - 線程何時釋放對象的鎖定

[英]Thread synchronization- When does a thread release the lock on an object

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會在線程一完成執行之前獲取一個鎖嗎? 有人可以解釋一個線程一次釋放堆棧對象上的鎖嗎?

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)

結果1 1 2 - 您可以清楚地看到它是如何繼續的。

當你說:

根據我的理解,當線程1啟動時,它會獲取push方法的鎖定。

這不太對,因為鎖不僅僅是推方法。 push方法使用的鎖是在調用push的MyStack2實例上。 方法pop和toString使用與push相同的鎖。 當一個線程在一個對象上調用任何這些方法時,它必須等到它可以獲得鎖定。 調用push的中間線程將阻止另一個線程調用pop。 線程調用不同的方法來訪問相同的數據結構,對訪問該結構的所有方法使用相同的鎖來防止線程同時訪問數據結構。

一旦線程在退出同步方法時放棄鎖定,調度程序就會決定哪個線程接下來獲得鎖定。 你的線程正在獲取鎖並讓它們多次運行,每次釋放鎖時,調度程序都會做出決定。 你不能做出任何關於哪些將被選中的假設,它可以是任何一個。 多線程的輸出通常是混亂的。

看起來你可能對synchronized和yield關鍵字的確切含義有些混淆。

同步意味着一次只能有一個線程進入該代碼塊。 想象一下它作為一扇門,你需要一把鑰匙才能通過。 進入的每個線程都使用唯一的密鑰,並在完成后返回它。 這允許下一個線程獲取密鑰並在其中執行代碼。 它們在同步方法中的長度並不重要,一次只能輸入一個線程。

Yield給編譯器建議(並且只是建議)當前線程可以放棄其分配的時間而另一個線程可以開始執行。 然而,並非總是這樣。

在您的代碼中,即使當前線程向編譯器建議它可以放棄其執行時間,它仍然保持同步方法的鍵,因此新線程無法進入。

不可預知的行為來自於產量並沒有像你預測的那樣放棄執行時間。

希望有所幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM