ConcurrentLinkedQueue:
一種「併發安全」的 Queue(佇列),多個執行緒可以同時寫入而不會互相干擾。
這樣就不用再加 synchronized,寫法更簡單。
synchronized 概念:
用關鍵字 synchronized 來保護程式區塊或方法,確保同一時間只有一個執行緒能存取共享資源。
缺點:效能可能比較差,因為鎖是「粗的」(一次鎖住整段程式碼),導致執行緒容易「排隊等鎖」。
只是一個小區塊要保護 -> synchronized
想要整個資料結構 ->(List/Map/Queue)能自動處理同步 Concurrent Collections
高併發、效能要求高 -> Concurrent Collections(效能更好)
資料結構簡單,偶爾才需要同步 -> Collections.synchronizedList() 也能用
任務直接往 Queue 塞結果
每個執行緒完成後 results.add(...),不用等 Future。 主執行緒最後再統一輸出。
有序性:
Queue 裡的順序是「任務完成的順序」,不是 port 的自然排序。
如果要保證 輸出依照 port 順序,就要用 Day16 那種 Future + List 的方法。
AtomicInteger:
一樣用來統計開啟的 port 數量,避免 race condition。
之前是用 List<Future> results 來收集掃描結果,最後再用迴圈 f.get() 一個個取出。
問題是:
如果結果數量很多,f.get() 會讓你等到全部任務結束才能看到結果。
如果多個執行緒同時存取同一個集合 (像是 ArrayList),會出現 執行緒安全問題 (Thread-safety issue)。
解決方式 → 使用 Java 提供的並發集合 (Concurrent Collections)
例如:
ConcurrentLinkedQueue → 無限長度、FIFO,適合即時收集結果
CopyOnWriteArrayList → 適合讀多寫少
ConcurrentHashMap → 適合 key-value 共享資料
BlockingQueue → 任務排程、結果同步
用GPT幫我生成的對照表格
面向 | Day17: Future + List | Day18: Concurrent Collection |
---|---|---|
結果儲存 | List<Future<String>> results → 先存 任務的 Future |
ConcurrentLinkedQueue<String> → 直接存 字串結果 |
取得方式 | f.get() 等待 Future 完成後再拿結果 (會阻塞) |
任務完成後馬上 results.add(...) ,可立即取得 |
時間點 | 全部任務跑完後才收集並輸出結果 | 邊跑邊加,程式可邊掃描邊輸出結果 |
同步性 | ArrayList 不是 thread-safe,但因為只在 main thread 收集結果,所以還算安全 |
ConcurrentLinkedQueue 是 thread-safe,允許多執行緒同時寫入 |
程式結構 | 需要兩段流程: (1) 提交任務 (2) 等待 Future 並取結果 | 一段流程即可:提交任務時就能把結果丟進集合 |
適合情境 | - 要保證所有結果都完整拿到再處理 - 結果數量不大 | - 需要即時處理 (像掃描器、即時監控) - 結果數量大,效率更好 |
今天我真正理解了「併發安全」在 Java 裡的意義。以前看到 synchronized 就覺得有點抽象,還不太懂什麼時候該用它,什麼時候不用。今天透過 ConcurrentLinkedQueue、AtomicInteger 等並發集合,我明白了:
小範圍共享資源 → 用 synchronized 就好,保護一個小區塊,不會造成大幅效能損耗。
整個資料結構需要多執行緒同時操作 → 直接用併發集合就能自動處理同步,寫程式更簡單,也不用擔心 race condition。
以前用 Future 收結果,要一個個 get() 等任務完成,常常要等很久,效率很低。現在用併發集合,任務完成就直接丟進 Queue,主程式最後統一取結果就好,順序雖然是任務完成的順序,但效率高很多。
今天我覺得自己對「多執行緒收集結果」有更直觀的理解了,也懂得該怎麼選擇工具:小區塊就鎖,大資料結構就用併發集合,必要時用 Atomic 變數保護統計。感覺寫起程式來會更安全又不拖慢效能,這個概念很實用。