java应用程序包含3个对象。一个Student对象,包含有关学生的所有必需信息。 List,ArrayList,用于保存Student对象。还有一个RandomAccessFile,可以读取学生信息并将其写入文件。
如果多个线程将访问列表,添加,删除和修改Student对象,同时写入磁盘,我应该在添加,删除,修改和写入磁盘时同步哪个对象? Student,ArrayList或RandomAccessFile?如果我选择一个而不是另一个会有性能增益/损失吗?
在一本书中,我正在读取同步是在Student对象上完成的,但是我找到了一个在RandomAccessFile上完成同步的在线源代码?
我非常感谢您的澄清
答案 0 :(得分:1)
在性能方面,您同步的对象没有区别(只是确保它始终是相同的实例,在所有情况下)。然而...
你最好提供一个隐藏列表和值(学生)的类的实例(比如StudentData)并提供你需要的基本操作:add(Student),get(String studentId)?, remove(字符串studentId)?等等(旁白:如果你的学生对象有 ids,你可能最好将它们放在地图中,而不是列表中。)
所有这些方法都将被声明为synchronized(因此会在StudentData实例上同步),因此每个方法都会以原子方式完成,然后由另一个线程更改为整个StudentData结构。
两个警告:
如果您的Student对象是可变的(即有getter / setter),则从get()返回时需要返回Student实例的副本,并将传入的Student值中的数据复制到更新()/ add()上的StudentData中的列表(或映射)。否则,可以在StudentData方法完成之后通过对它的引用来修改Student的状态(允许在同步代码之外进行更新)。
您不应该在同步代码中执行I / O.如果这样做,阻止I / O的东西将阻止同步锁完成,一切都将停止,直到I / O解除阻塞。 (实际上,您应该始终将同步代码中完成的工作限制在最低限度。)如果您的数据集不是太大,一种方法是同步获取整个数据集的副本,然后在同步代码,将此全部写入磁盘。
但是,如果您打算使用RandomAccessFile来管理某种磁盘上的记录结构(而不是比如保存的数据文本表示 - 例如XML,JSON),为什么不呢?改为使用RDBMS?
答案 1 :(得分: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);
}
}
话虽如此,保罗提出了一个关于不在同步块中做IO的好处。将IO移出同步块的锁的替代方法,虽然它更复杂,但是将Command模式与从队列执行命令的单个线程结合使用。一个命令可以将学生添加到列表中,后续命令可以将列表写入文件。您可以拥有尽可能多的线程,将新命令放入队列,并且它们都将按照它们到达的顺序执行。再次,在这里应用SRP。每个命令只应该做一件事。
编辑:
所以虽然这可能比你想要上课更多,但这详细说明了命令队列的想法。
每个命令都是runnable,run()方法可以执行您想要的操作 - 添加学生,写入磁盘等等。一旦启动执行程序线程,它就会在那里等待新命令显示在队列中。如果没有命令,它会阻塞,但其他线程可以继续执行。如果有命令,则它与其他线程并行运行。