2017-04-15 40 views
0

我有數據的一個實例,在2線程之間共享我的類服務器:共享變量沒有被修改

public class Server{ 

    public static void main(String[] args){ 
    Data data = new Data(); 
    Thread t1=new Thread(new ClientService(data)); 
    Thread t2=new Thread(new PromoService(data)); 
    t1.start(); 
    t2.start(); 
} 

這裏是我的課的有關部分:

public class ClientService implements Runnable{ 

    public Data data; 


    public ClientService(Data data){ 

    this.data = data; 
    } 

    public void run(){ 
    this.data.newClient(t[1],Integer.parseInt(t[2]),Integer.parseInt(pass),ia); 
} 

在這裏,讓我們假設t是一個String數組,它是一個InetAdress並傳遞一個字符串(我已經移除了創建這些變量以使其更簡單的代碼部分)。

public class Data implements Serializable{ 

public ArrayList<ClientData> listClient; 

public Data(){ 
    this.listClient = new ArrayList<ClientData>(); 
} 

public synchronized boolean newClient(String id,int port,int pass,InetAddress ia){ 

    for(int i = 0 ; i < listClient.size(); i ++){ 
    if(listClient.get(i).id.equals(id)){ 
     return false; 
    } 
    } 
    ClientData ncl = new ClientData(port,ia,id,pass); 
    this.listClient.add(ncl); 
    return true; 
} 

在newClient()之前,listClient不包含任何內容(size = 0)。打電話後,我嘗試打印listClient和大小已成爲1,但包含的元素爲空。我不明白:阻礙新的ClientData對象被添加到這個共享變量的東西? 當我嘗試時,我確保此線程是唯一一個在此時訪問或修改實例。

+0

這只是一個想法,但試着將'listClient'聲明爲'final'或'volatile'。這可能是編譯器做了一些有趣的事情,但我懷疑它同步應該確保自己的良好行爲。此外,之後訪問'listClient'的線程(以檢查其中的內容)也需要在'data'上同步。 'synchronized(data){System.out.println(data.listClient); }' – Radiodef

+0

我剛剛添加了這一行,它的工作原理非常感謝!但是我不明白爲什麼它會改變任何東西,如果我確定沒有其他人在此刻正在調試它 – StuYYY

+0

null的舊值仍然可以保存在寄存器中,而不是從RAM加載。爲了減少遇到像這樣的意外問題的風險,我會盡量不要手動執行任何同步,而是使用'java.util.concurrent.atomic.AtomicReference'的實例來保存我的共享變量。 – SpiderPig

回答

0

根據我的意見,當您從listClient讀取時需要同步,因爲它會強制線程之間的內存一致性。在沒有同步的情況下,某些線程A可以對某些變量x進行更改,而另一個線程B不需要看到對x的更改。同步強制線程查看對共享內存的更改。

從技術上講,這是因爲CPU有一個臨時存儲變量的本地緩存。同步強制CPU從主內存加載變量,而不是查看可能過期的緩存。

因此,synchronized (data) { /* look at what's in data.listClient */ }強制讀者線程看到listClient在同步方法newClient期間做出的任何更改。 (同步實例方法與synchronized (this)相同。)

從您的描述中,聽起來像讀者線程看到列表的size得到增加,但從未看到對備份數組的更改。