2017-04-15 41 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得到增加,但从未看到对备份数组的更改。