2014-03-05 54 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