2015-05-15 28 views
1

我有一個類如下:爲什麼在使用getter和setter的時候線程不安全的類無法工作?

public class BoolFlag 
{ 
boolean flag; 

public BoolFlag() 
{ 
    flag=false; 
} 

public synchronized void setFlag(boolean flag) 
{ 
    this.flag=flag; 
} 

public synchronized boolean getFlag() 
{ 
    return flag; 
} 
} 

如果我不設置getter和setter進行同步,這將導致該問題如下:

當我設置稱爲BoolFlag的對象在ImageReady中一個buttonclick listiner:

if (status == JFileChooser.APPROVE_OPTION) 
      { 
       try 
       { 
        System.out.println("File chosen"); 
        imgFile=chooser.getSelectedFile(); 
        image.setImg(ImageIO.read(imgFile)); 
        imageready.setFlag(true); 

       } 
       catch(Exception e) 
       { 
        System.out.println("file not opened"); 
       } 
      } 

然後把它在另一個線程:

public void run() 
{ 
    boolean running=true; 
    while(running) 
    { 
     // System.out.println("Img sender running"); 
     if(imageready.getFlag()) 
     { System.out.println("trying to send img"); 
      try 
      { 
        OutputStream outputStream = img_s.getOutputStream(); 
        ImageIO.write(image.getImg(), "jpg", outputStream); 

        System.out.println("Send image"+System.currentTimeMillis()); 
        outputStream.flush(); 
        outputStream.close(); 
        imageready.setFlag(false); 

      } 
      catch(Exception e) 
      { 
       System.out.println("image client send failed"); 
       imageready.setFlag(false); 
      } 
     } 
    } 
} 

我期望是選擇文件後,ImageReady的應設置爲true,那麼線程誰得到的標誌執行塊中的語句:

if(imageready.getFlag()){...send image...} 

但它不會進入塊中的偶數儘管imageready被設置爲true。

正如你可能會注意到,我有一份聲明被註釋掉,如果塊的前面:

// System.out.println("Img sender running"); 

如果我不註釋掉,在if塊中的語句將被執行。把其他無關的陳述,如睡眠(100)可能會導致相同的效果。如果我在if(imageready.getflag())處放置一個斷點,然後逐步執行它,它也會進入該塊。

如果我設置getter和setter被同步,這些問題不會發生。 看來這是關於胎面安全類的東西,但我不明白爲什麼它很重要,即使我只是使用getter和setter。

回答

1

您的獲取訪問器可能被內聯,因此它實際上只是一個字段讀取,並且運行時可能會優化掉出現在循環中的簡單字段讀取,從而使該值僅被有效讀取一次。

您可以通過讀取volatile值或者如您注意到的,在寫入和讀取標誌時使用​​來強制執行內存屏障來避免此問題。

就你而言,我只需要使用AtomicBoolean來代替你的BoolFlag類。

在附註上,我會避免在第二個線程上「準備好圖像」標誌的忙碌旋轉。考慮使用等待通知機制,例如,計數爲1CountDownLatch

1

是的,這是由於線程通信,你試圖實現的是Product-Consumer problem

很少有什麼問題與您的代碼:

1)寫入圖像文件,該線程類,不堵塞內部getFlag()方法,讓你的while循環可能運行了很多保持線程處於活動狀態。取而代之的是阻止getFlag()中的線程,並利用wait()notifyAll()方法進行有效的線程通信。

Java example of producer-consumer problem。你需要做類似的事情來管理你的案例中的線程。

+0

你還可以使用Thread.join – ControlAltDel

+0

對不起,其他? –

+0

在生產者 - 消費者問題中,生產者和消費者會修改同一個對象。但是我的情況有點不同:一個線程正在修改imageready的值,而另一個線程正好修改了它的值。 –

0

同步不提供編寫線程安全類的工具。在多線程環境中,如果一個對象在多個線程之間共享,那麼就沒有可能影響程序正確性的問題,例如數據競爭,髒讀,過時的值。

爲了優化您的代碼java將變量數據保存在內存緩衝區中以便更快地訪問。由於其他線程對該變量所做的更改可能不會立即反映,這就是爲什麼其他線程讀取該變量的前一個值,因此在不久的將來無法讀取更新後的值。

您可以將您的變量定義爲volatile。易失性確保線程將獲得由Java內存模型提供的最近更新的變量值。

+0

但是if(imageready.getFlag())將繼續運行並繼續檢查imageready是否更改爲true,因爲它位於線程中的while(true)循環中。那麼,爲什麼它不會爲圖像準備好更新的值呢?謝謝。 –

+0

正如我所說的每個線程都有自己的堆棧區域和值存儲在寄存器中,所以一旦捕獲它...它可能無法獲得更新的值.... –