簡體   English   中英

有關同步方法,鎖和監視器的說明

[英]Clarifications about synchronized methods, locks and monitor

為了加深我對Java同步的了解,我的困惑始於以下聲明,取自此處

Java中的每個對象都與一個監視器關聯,線程可以鎖定或解鎖監視器。 [...]如果完成了[synchronized]方法主體的執行,無論是正常執行還是突然執行,都會在同一監視器上自動執行解鎖操作。

因此,我正在想象一個像圓頂的監視器,它覆蓋整個對象,並防止兩個線程同時訪問它。 更清楚地說,我認為

class MySync{
    synchronized void foo(){}
    void bar(){}
    String x;
}

如果threadA訪問並運行(同步) foo()方法5秒鍾,它將“激活圓頂”,從而阻止threadB訪問對象的任何其他成員,如barx

我編寫這些代碼行只是為了進行一些測試,然后發現我錯了……

public class Main{

    public static void main(String args[]) throws InterruptedException{
        new Main();
    }

    MySync ms = new MySync();               
    RunnableA r1=new RunnableA(ms);
    RunnableB r2=new RunnableB(ms);

    Main() throws InterruptedException{
        r1.start();
        Thread.sleep(1000);
        r2.start();
    }

}

class MySync{

    synchronized void foo(String tab){

        System.out.println(tab+Thread.currentThread().getName()+" in foo()");

        if("A".equals(Thread.currentThread().getName())){
            System.out.println(tab+Thread.currentThread().getName()+" Waiting 5 seconds");
            try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
        }

        System.out.println(tab+Thread.currentThread().getName()+" out foo()");

    }

    void bar(String tab){
        System.out.println(tab+Thread.currentThread().getName()+" in bar()");
        System.out.println(tab+Thread.currentThread().getName()+" out bar()");
    }

}

class RunnableA implements Runnable{

    String tab="";
    Thread t=new Thread(this);
    MySync ms;

    RunnableA(MySync ms){
        this.ms=ms;
        t.setName("A");
    }

    void start(){t.start();}

    @Override
    public void run(){
        System.out.println(tab+"A Running ");
        ms.foo(tab);
        System.out.println(tab+"A End running");
    }
}

class RunnableB implements Runnable{

    String tab="    ";
    Thread t=new Thread(this);
    MySync ms;

    RunnableB(MySync ms){
        this.ms=ms;
        t.setName("B");
    }

    void start(){t.start();}

    @Override
    public void run(){
        System.out.println(tab+"B Running ");
        ms.bar(tab);
        System.out.println(tab+"B End running");
    }
}

這是輸出:

A Running 
A in foo()
A Waiting 5 seconds    //threadA stuck in syncronized foo() method
    B Running          //threadB accesses bar() method anyway
    B in bar()
    B out bar()
    B End running
A out foo()
A End running

我的問題是:

Java中的每個對象都與一個監視器關聯是什么意思? 那不是有點模棱兩可嗎? 如果我也聲明了同步的bar() ,則會得到我期望的行為。 這是否意味着我的假想圓頂僅一次覆蓋所有同步方法(即,所有同步方法都使用一個鎖)?

同步是一種開發人員工具,用於使線程之間能夠協調地訪問資源。

Java語言規范規定

synchronized語句代表執行線程獲取互斥鎖(第17.1節),執行一個塊,然后釋放該鎖。 當執行線程擁有該鎖時,其他任何線程都無法獲取該鎖。

在您的情況下,線程A獲取鎖並執行foo (已實現為需要鎖)。 然后,線程B執行尚未實現以要求鎖定的bar 因此, B未被阻止。

再次從規格

獲取與對象相關聯的鎖本身並不能防止其他線程訪問該對象的字段或調用該對象上的未同步方法。 其他線程也可以以常規方式使用同步方法或同步語句來實現互斥。

同步看作訪問的東西必須通過同步目標來完成雙方之間的協議。 如果某些代碼忽略這個協議,並直接訪問的東西 ,它可能會導致在提到線程干擾和內存一致性錯誤的同步Java教程

是的,虛構的球頂僅覆蓋該對象的所有同步方法,以及該對象上顯式同步的代碼段,例如:

,,,
synchronize(this) {
doSomething();
}

並非代碼的所有部分都是線程安全敏感的,因此某些方法可以在同一對象中“不同步”,從而加快了執行速度。

“ Java中的每個對象都與一個監視器關聯是什么意思?”。

這意味着您在每個對象上都有可用的監視器。 您可以通過在對象上聲明同步方法或使用syncnize(obj)方法來訪問它。 因此,您無需引入另一個提供監視器功能的“實體”。 對於適當的監視器,您可以訪問每個對象的wait()和notify()方法。

syncrhonize(obj) {
 while(someConditionIsNotTrue) {
 obj.wait();
 }
}

因此,只要您的代碼邏輯需要對對象的獨占訪問權,就可以使用內置監視器並對其調用同步。

為了提供線程安全,所有零件都必須按照相同的規則進行游戲,並在需要時同步使用。 為了避免由於遺漏而導致的錯誤,存在更高級別的並發機制,例如AtomicInteger和ConcurrentCollections。

為了處理這種圓頂機制,每個Java對象都有一個類型為lock flag ,並且使用synchronized可以根據以下模式與該lock標志進行交互。

同步之前鎖定標志和對象

調用同步(此)后,線程將保留鎖定標志,直到線程完成處理或經歷某些特定的中斷情況為止。

線程獲取的鎖定標志

雖然鎖標志由第一線程獲得,如果第二個線程來了,嘗試訪問由引用的對象 this是.won't能夠因為鎖標志是不存在的。 因此,線程調度程序會將新線程放入等待對象鎖定標志的線程池中。 它會一直停留到釋放鎖定標志為止,並且可能會立即訪問該對象,也可能不會立即訪問該對象,具體取決於線程調度程序的調度決定

等待池

發生以下事件時,將釋放鎖定標志

  • 線程通過同步代碼塊的末尾
  • 同步代碼塊引發中斷,返回或異常時

為了正確使用同步,務必確保

  • 對微妙數據的所有訪問均已synchronized (您應該已完成)
  • 所有受同步保護的微妙數據必須是私有的

暫無
暫無

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

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