2014-03-05 49 views
0

一个java应用程序有3个对象等等。一个Student对象,其中包含有关学生的所有必需信息。包含Student对象的List,ArrayList。以及一个读取和写入学生信息到文件的RandomAccessFile。我应该同步哪个对象?

如果多个线程将访问列表,添加,删除和修改Student对象,同时写入磁盘,应该在添加,删除,修改和写入磁盘时同步哪个对象?学生,ArrayList或RandomAccessFile?如果我选择了另一个,是否会有性能增益/损失?

在一本书中,我正在阅读同步是在Student对象上完成的,但是我发现了一个在线源代码,该同步在RandomAccessFile上完成了?

我将非常感谢您的澄清

+1

__你想什么?为什么? –

+0

这是一个标准的'ArrayList',还是一个['Collections.synchronizedList(ArrayList)'](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html# synchronizedList(java.util.List))/ ['CopyOnWriteArrayList'](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CopyOnWriteArrayList.html)? – Powerlord

+0

一个标准的ArrayList – WhatIf

回答

1

性能方面并没有区别,可以同步什么对象(只需确保它始终是相同例如,在所有的情况下)。然而...

您最好提供隐藏列表和值(学生)并提供您需要的基本操作的类的一个实例(如StudentData):add(Student),get(String studentId )?删除(字符串studentId)?等等,等等(旁白:如果你的学生对象 IDS,你可能会更好地把它们放在一个地图,而不是一个列表)

所有这些方法会。被声明为同步的(并且因此会在StudentData实例上同步),因此每个线程在由其他线程到任何整体StudentData结构的任何后续更改之前都将以原子方式完成。

两个注意事项:

  • 如果你的学生对象是可变的(即具有getter/setter方法),就需要从GET(返回时,从通过返回的学生实例的副本),并复制数据在学生值中添加到update()/ add()中的StudentData中的列表(或地图)中。否则,在StudentData方法结束后,可以通过引用它来修改Student的状态(允许在同步代码之外进行更新)。

  • 您不应该在同步代码中执行I/O操作。如果这样做,阻止I/O的操作将阻止同步锁定完成,并且一切都将停止,直到I/O解除锁定。 (实际上,您应该始终将同步代码中完成的工作限制在最低需求的范围内。)一种方法是,如果数据集不太大,则应同步获取整个数据集的副本,然后在同步的代码,将其全部写入磁盘。但是,如果您打算使用RandomAccessFile来管理某种磁盘上的记录结构(而不​​是像数据的保存文本表示 - 例如XML,JSON),那么为什么不直接使用它呢?为什么不使用RandomAccessFile?一个RDBMS呢?

+0

+1你说得很好。对于RandomAccessFile,它可能是他自己推出的课程练习的要求,但如果是为了工作,那么是的,使用现有工具会更简单。 –

+0

[[您不应该在同步代码中执行I/O操作。 ]]一个问题 - 如果只有一个文件在其中写入学生列表,那么我应该在什么地方完成I/O,如果不是同步代码。 – RuntimeException

+0

@SatishMotwani - 如果是课堂练习,你可能不想担心。如果是真正的项目,则使用单独的命令处理线程解决此问题。 –

1

考虑到单一责任原则适用于这些成员变量,为什么尝试使用其中的任何服务于两个目的:既来存储数据,并锁定?我想创建一个单独的成员变量只是锁:

class Student { 
    private String name; 

    Student(String name) { 
     this.name = name; 
    } 

    String getName() { 
     return name; 
    } 
} 

private List<Student> students = new ArrayList<Student>(); 
private final Object lock = new Object(); 

... 

public void updateList(){ 
    Student newGuy = new Student("Joe"); 
    synchronized (lock){ 
     students.add(newGuy); 
    } 
} 

话虽如此,保罗对synchronized块内没有做IO好点。除了将IO移出同步块外的锁的另一种方法是使用Command模式与执行来自队列的命令的单个线程的组合。一个命令可以将一个学生添加到列表中,随后的命令可以将该列表写入文件。您可以拥有尽可能多的线程,只要您将新命令投入到队列中,就可以按照它们到达的顺序执行。再次,在这里应用SRP。每个命令只应做一件事。

编辑:

因此,尽管这比你想咬掉类可能更多,这种阐述了命令队列的想法。

enter image description here

每一个命令是可运行的,你想要的run()方法做 - 增加了一个学生,写入磁盘,等等。一旦启动执行程序线程,它就会等待新的命令显示在队列中。如果没有命令,则会阻塞,但其他线程可以继续执行。如果有命令,它将与其他线程并行运行。

+0

如果我遵循你的方法,那么应该在修改数据的Student类中声明单独的成员变量。编写一个类作为锁是否有意义?让它成为一个单身课程。这会有什么好处,或者只是成员变量就足够了。 – WhatIf

+0

“将代码编写为锁是否有意义?”如果你的意思是创建一个新类来充当锁,否。只需使用Object。而且我不会把锁放在Student中,我会将其放入具有Student对象列表的类中,如上所示。 –

+0

这是否需要所有同步?还是我还必须同步修改Student类的变量的方法? – WhatIf