我有一個使用 TCP 的簡單服務器套接字應用程式,它允許多個客戶端連接到服務器并從 ArrayList 存盤和檢索資料。每次客戶端連接被服務器接受時,都會創建一個新執行緒來處理客戶端活動,并且由于所有客戶端必須與相同的資料結構互動,因此我決定創建一個靜態 ArrayList 如下:
public class Repository {
private static List<String> data;
public static synchronized void insert(String value){
if(data == null){
data = new ArrayList<>();
}
data.add(value);
}
public static synchronized List<String> getData() {
if(data == null){
data = new ArrayList<>();
}
return data;
}
}
因此,每次客戶端插入一個值或讀取他們剛剛呼叫的串列Repository.insert(value)或Repository.getData()從它們各自的執行緒中讀取時。
我的問題是:
- 使這些方法同步就足以使操作執行緒安全嗎?
- 這個靜態串列有什么架構或性能問題嗎?我還可以在服務器類(接受連接的那個)中創建 List 的一個實體,并通過建構式向執行緒發送一個參考,而不是使用靜態的。這是不是更好?
- 可以
Collections.synchronizedList()為這樣一個簡單的任務增加任何價值嗎?有必要嗎? - 在這種情況下如何測驗執行緒安全性?我嘗試只創建多個客戶端并讓他們訪問資料,一切似乎都有效,但我不相信......這是這個測驗的一小段:
IntStream.range(0,10).forEach(i->{
Client client = new Client();
client.ConnectToServer();
try {
client.SendMessage("Hello from client " i);
} catch (IOException e) {
e.printStackTrace();
}});
//assertions on the size of the array
提前致謝!:)
uj5u.com熱心網友回復:
- 是的,請參閱https://stackoverflow.com/a/2120409/3080094
- 是的(請參閱下面的評論),是的。使用一個物件(如果你喜歡單例)優于靜態方法(后者通常更難維護)。
- 沒有必要但首選:它可以避免您犯錯誤。此外,而不是:
private static List<String> data;
您可以使用
private static final List<String> data = Collections.synchronizedList(new ArrayList<>());
這有三個好處:final確保所有執行緒都看到這個值(執行緒安全),您可以洗掉檢查空值的代碼(這可能容易出錯)并且您不再需要synchronized在代碼中使用串列本身現在是同步的。
- 是的,有一些方法可以改進這一點,使您更有可能發現錯誤,但是當涉及到多執行緒時,您永遠無法完全確定。
關于“架構或性能問題”:串列的每次讀取都必須同步,因此當多個客戶端想要讀取串列時,它們都將等待鎖定以讀取整個串列。由于您只是在末尾插入并閱讀串列,因此您可以使用ConcurrentLinkedQueue. 這種“并發”型別(即不需要使用synchronized-佇列是執行緒安全的)在讀取整個串列時不會鎖定,多個執行緒可以同時讀取資料。此外,您應該隱藏您可以執行的實作的詳細資訊,例如,使用Iterator:
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
private final Queue<String> dataq = new ConcurrentLinkedQueue<>();
public Iterator<String> getData() {
return dataq.iterator();
}
關于“測驗執行緒安全”:關注需要執行緒安全的代碼。使用客戶端連接到服務器來測驗Repository代碼是否是執行緒安全的是低效的:大部分測驗將只是等待 I/O 而不是同時實際使用Repository。撰寫一個專門的(單元)測驗Repository班級。請記住,您的作業系統確定執行緒何時啟動和運行(即您的執行緒啟動和運行代碼只是作業系統的提示),因此您需要測驗運行一段時間(即 30 秒)并提供一些輸出(日志記錄)以確保執行緒同時運行。輸出到控制臺應該只在測驗結束時顯示:在 Java 中輸出到控制臺 (System.out) 是同步的,這反過來可以使執行緒一個接一個地作業(即不在被測類上同時作業) . 最后,您可以使用 a 改進測驗java.util.concurrent.CountDownLatch,讓所有執行緒在并發執行下一個陳述句之前同步(這提高了發現競爭條件的機會)。對于這個已經很長的答案要解釋的有點多,所以我會給你一個(公認的復雜)示例(關注tableReady變數的使用方式)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/343891.html
下一篇:如何計算重復的物件屬性?
