2014-01-13 85 views
1

我寫了一個擴展線程的內部私有類的類。我的外部類啓動此線程的一個實例,並且該線程訪問循環內的外部類的字段。但是,外部代理可能會調用外部類的方法來修改外部類的字段。這些方法必須與線程內部的循環同步,以便修改不會干擾循環。爲什麼兩個同步塊像我一樣提供了不同的監視器對象,當兩個監視器域引用相同的對象時?

我一直在使用外部類(Android SurfaceHolder)的字段上的「synchronized」塊進行同步,將此對象傳遞給內部類並將其引用存儲爲內部類中的字段,然後同步在線程循環中的內部類字段上。然而,這導致外部類方法在內部類應該被鎖定時運行的行爲!我試圖刪除內部字段和內部類鎖定在外部類完全相同的領域,並且一切正常。

所以,這裏是一個問題:我通過檢查==運算符並查看字符串表示來驗證內部和外部字段指向的對象是相同的對象,那麼爲什麼同步塊在此行爲就像我使用兩個不同的對象一樣?也許我對同步塊的工作方式有一個基本的誤解?

編輯:

好吧,我得到了很多downvotes,但評論者似乎只是希望有更多的細節,我決心要明白我沒有得到這裏。我會回覆每條評論。在這裏,開始,是我在做什麼的例子:

class Outer { 
    private Object lock; 
    private Foo foo;   

    public Outer() { 
     lock = new Object(); 

     // The thread is actually started in an Android callback method, 
     // but I'll start it here as a demonstration 
     InnerThread thread = new InnerThread(lock); 
     thread.setRunning(true); 
     thread.start(); 
    } 

    private void modifyFoo() { 
     synchronized(lock) { 
      Log.d("outer", "locked outer"); 
      foo.bar(); // Has some effect on foo 
     } 
     Log.d("outer", "released outer"); 
    } 

    private class InnerThread extends Thread { 

     private volatile boolean running = false; 
     private Object lock; 

     public InnerThread(Object lock) { 
      this.lock = lock; 
     } 

     private void setRunning(boolean running) { 
      this.running = running; 
     } 

     @Override 
     public void run() { 
      while(running) { 
       synchronized(lock) { 
        Log.d("inner", "locked inner"); 
        foo.blah(); // Has some effect on foo 
       } 
       Log.d("inner", "released inner"); 
      } 
     } 

    } 
} 

意外的行爲是,當我稱之爲modifyFoo()方法與線程運行,我看到了下面的日誌輸出:

locked inner 
released inner 
locked inner 
released inner 
locked inner 
locked outer 
released outer 
released inner 

一個響應指出「你永遠不應該擴展線程」和「你似乎有一個對象在一個字段和外部對象本身」。首先,我擴展了Thread,因爲它是Android SurfaceView示例中使用的方法。我不知道如何重寫run()方法;我應該將一個runnable傳遞給線程嗎?其次,正如我所理解的那樣,內部和外部類中的lock字段都持有對lock = new Object();行上創建的同一實例的引用。我不是問這應該如何組織;我正在問具體爲什麼同步塊看起來像查看對不同對象的同一對象的這兩個引用。

要添加一些上下文,這是一個使用Android的SurfaceView類的項目。我儘量靠近Android提供的LunarLander項目,其中他們用於鎖定的SurfaceHolder對象被傳遞到線程構造函數中,並通過引用存儲在其中。這可能是奇怪結構的原因。

+3

如果你可以發佈你的代碼,那就太好了。 –

+3

理想情況下,作爲一個簡短但完整的程序演示該問題... –

+0

請閱讀[如何創建MCVE](http://stackoverflow.com/help/mcve)。 – assylias

回答

3

爲什麼兩個同步塊像我一樣提供了不同的監視對象,當兩個監視器字段引用同一個對象時?

沒有看到代碼,我會假設他們真的是不同的對象,即使你認爲他們應該是相同的。

我寫了一個擴展線程的內部私有類的類。

你不應該擴展Thread。這會導致您在代碼中不需要的各種邊界情況。

我一直在使用在外類(一個Android SurfaceHolder)的視野中的「同步」塊同步,並傳遞該對象進入內類和存儲的引用它作爲一個字段在內部類然後在線程循環中的內部類字段上進行同步。然而,這導致外部類方法在內部類應該被鎖定時運行的行爲!

這聽起來像是你有兩個例子給我。您似乎在一個字段中有一個對象,並且外部對象本身。

我試圖刪除內部字段和內部類鎖定在外部類完全相同的領域,並且一切工作正常。

這取消了對我提到的兩個的。

也許我對同步塊的工作方式有一個根本性的誤解?

你必須鎖定在同一個對象實例。你不要鎖定字段或類或方法。我建議創建一個只用於鎖定的鎖定實例,這就是你所用的全部。這樣,如果它是外部類的字段,則不需要將它傳遞給內部類。

class Outer { 
    final Object lock = new Object(); 

    public void method() { 
     synchronized(lock) { 
      // do something 
      lock.notifyAll(); 
     } 
    } 

    class Inner implements Runnable { 
     public void run() { 
       while(!Thread.currentThread().isInterrupted()) { 
       synchronized(lock) { 
        lock.wait(); 
        // check if anything changed. 
       } 
       } 
     } 
    } 
} 

編輯:

我驗證對象在尖由內部和外部兩個領域是同一個對象通過檢查==操作符

如果我複製錯被引用的對象用於鎖定並將其與自身進行比較,這也將是真實的。如果我複製一個引用並有另一個引用我知道應該是相同的,爲什麼要複製它呢?

EDIT2:這是我會怎麼寫。

class Outer { 
    final Object lock = new Object(); 
    private Foo foo;   

    public Outer() { 
     // The thread is actually started in an Android callback method, 
     // but I'll start it here as a demonstration 
     Thread thread = new Thread(new InnerRunnable()); 
     thread.start(); 
    } 

    private void modifyFoo() { 
     synchronized(lock) { 
      Log.d("outer", "locked outer"); 
      foo.bar(); // Has some effect on foo 
      Log.d("outer", "released outer"); 
     } 
    } 

    class InnerRunnable implement Runnable { 
     private volatile boolean running = true; 

     void setRunning(boolean running) { 
      this.running = running; 
     } 

     @Override 
     public void run() { 
      while(running) { 
       synchronized(lock) { 
        Log.d("inner", "locked inner"); 
        foo.blah(); // Has some effect on foo 
        Log.d("inner", "released inner"); 
       } 
      } 
     } 
    } 
} 

(可選)您可以放下鎖並只使用foo

+0

仍然...'我通過檢查==運算符驗證內部和外部字段指向的對象是相同的對象 –

+0

@SotiriosDelimanolis如果我複製錯誤的引用對象進行鎖定並將其與自身進行比較,它會也是如此。如果他複製一份參考文獻並有另一份參考文獻,他知道應該是相同的,爲什麼他首先複製它? –

+0

是的,可能。在你的回答中大膽說明第一條語句:) –

相關問題