簡體   English   中英

兩個線程如何“進入”“同步”方法

[英]How can two threads be “in” a “synchronized” method

我真的是Java兼容的新手,我正在嘗試實現以下規范:

  • 我們有一個停車場,有一些公園景點
  • 每輛車都代表一條線,無休止地改變汽車狀態從駕駛 - 停車。 每輛車都有自己的停車位。
  • 當汽車處於停車狀態時,它會試圖停在一個地方(沒必要他的位置)。 如果該地點是免費的,那么它停在其他地方,它將跳過這個停車階段並返回開車。
  • 除非現場車主想要停車,否則該車仍然在現場。

這不是確切的規范,但我唯一的問題是:

我無法讓汽車在轉彎時跳過。 如果兩輛車選擇相同的位置,則一輛車停放,另一輛車等待直到公園空閑。 這不是我想要的行為。 我的第一個想法是簡單地將read和write同步到一個占用的變量:

class Car implements Runnable {
    private CarState state = CarState.driving

    run {
        while(true) {
            switch(state) {
            case driving: 
                System.out.println(this + " driving.");
                state = parking;
                break;
            case parking: Spot s = CarPark.getRandomSpot();

                if(s.willingToPark(this)) {
                    System.out.println(s + " occupied. " + this 
                    + " skip park turn.");
                } else {
                    s.park(this);
                } 
                state = driving;
            }
        }

    }
}

class Spot {
    private boolean occupied = false;
    private Car owner = new Car(...);

    synchronized boolean willingToPark(Car c) {
        if(occupied) {
            return true;
        } else {
            occupied = true;
            return false;
    }

    synchronized void park(Car c) {
        System.out.println(c + " parking at " + this);
        //don't care how this is implemented, just keep in mind
        //that it will enter in a loop until the owner came back.
        occupied = false;
    }
}

如果我用三輛車運行這個,那么我最終會讓car0停在spot1,car1停在spot0,car2正在spot0上等待,因為car1正在執行同步的停車場(Car c)。 如果願意同步,兩輛車可以在同一地點停車怎么辦?

謝謝

如果願意同步,兩輛車可以在同一地點停車怎么辦?

它實際上很簡單。 Car1捕獲spot0並開始在park()方法內的循環中等待所有者(您沒有提供代碼)。 在等待時,它擁有監視器並且不允許任何人在spot0上調用同步方法。
這就是為什么car2掛起了willingToPark()方法的原因。

問題在於park的循環。 想象一下:

  • 兩個線程都抓住了相同的Spot, s
  • 他們都試圖獲取ss監視器鎖; 只有一個會成功。
  • 成功的那個將導致park旋轉, 同時仍然持有鎖
  • 另一個耐心等待,直到鎖被釋放,以便它可以嘗試獲取它。

什么都沒有(沒有殺死JVM)會告訴線程無法停放,停止等待s 它會等到它可以獲得鎖定 - 這只會在第一個線程完成park循環后才會發生。

解決方案不是在park循環。 相反, 汽車應通過新方法unpark(Car)取消設置被occupied標志。 為了跨線程的內存可見性,還必須同步此方法。

現在數據流如下:

  • 兩個線程都抓住了相同的Spot, s
  • 他們都試圖獲取ss監視器鎖; 只有一個會成功。
  • 成功的那個設置occupied = true ,並立即返回然后釋放ss
  • 另一個獲得鎖定s ,並看到該地點被占用。

順便說一句,您甚至不需要synchronized方法。 您可以使用AtomicBoolean的compareAndSet方法,它允許您以原子方式檢查AtomicBoolean的值,並僅在其當前值與您預期的值相同時進行設置。 因此, return occupied.compareAndSet(false, true)表示“以原子方式檢查當前值;如果為false,則將其設置為true並返回true;如果為true,則保持原樣,然后返回false”。 這種行為很有用,但更先進一些。

使用AtomicBoolean標記停車位:

class Spot{
  public final AtomicBoolean flag = new AtomicBoolean(false);
}

在代碼中的其他地方,汽車線程中有一場比賽搶奪點:

if(spot.flag.compareAndSet(false,true)){
  // spot owned by current thread !!
  // for other threads, `compareAndSet` will fail because they expect it to be `false`.

  // visit store and buy stuff while car is parked.

  // time to go, release the spot
  spot.flag.set(false);
}else{
  // find another spot
}

完整示例: http//ideone.com/dw3LnV

這種方法是免費等待和鎖定免費的,您可以將點競賽邏輯放入while循環中,線程將繼續爭奪該標志。

進一步閱讀: http//www.ibm.com/developerworks/library/j-jtp11234/

盧卡,我認為你沒有全面了解同步。 等待循環是問題所在。 至於同步本身,它只能在對象上完成:

  • 具有方法synchronized使得它在同步this
  • 讓一段代碼synchronized(someObject) {...}使它在someObject同步

調用synchronized方法時,它不允許任何其他同步方法使用它同步的對象。 因此,通過使用該循環,您可以鎖定其他所有內容。 正如其他人所說,您應該盡可能以最短的時間使用同步來完成任務。

例如,您可以僅在park方法中同步取消停放部分:

void park(Car c) {
    System.out.println(c + " parking at " + this);
    //don't care how this is implemented, just keep in mind
    //that it will enter in a loop until the owner came back.
    synchronized (this) {
        occupied = false;
    }
}

暫無
暫無

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

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