限制併發 (Concurrency Limiting)
在 Day16 是用固定大小的執行緒池 (FixedThreadPool) 去同時掃 port。
但是如果開的執行緒太多 (例如 5000 個),可能會導致:
CPU 資源被搶光,記憶體不足 (Too many threads)
目標伺服器因為同時連線太多而「拒絕服務」。
常見併發問題(要特別小心)
1.競爭條件(Race condition)
多個執行緒同時讀寫同一筆共享資料,導致不可預期結果。
資料不一致 / 非原子操作
2.複合操作(read-modify-write)若未同步,會丟失更新。
3.死結(Deadlock)
兩個或多個 thread 各持有一把鎖並互相等待對方釋放 → 永遠卡住。
4.飢餓(Starvation)和活鎖(Livelock)
部分 thread 永遠拿不到資源,或不斷讓步但沒進展。
5.記憶體可見性(Visibility)
沒有適當同步時,一個 thread 的寫入不一定馬上被另一個看到。
6.資源耗盡(Resource exhaustion)
建太多 thread、過多 socket、過大 queue 會耗盡記憶體或 file descriptors。
節流 (Throttling)
「限制併發」是控制同時進行的數量,
而「節流」則是控制任務觸發的速度。
為什麼要節流?
避免在短時間內送出太多請求,讓伺服器無法負荷。
避免自己程式因為 I/O 過載而卡住。
安全性考量 (Security Considerations)
在寫「掃 port」這種程式時,必須要注意合法性
Port 掃描常被視為惡意行為。
只能掃「自己電腦」或「授權測試的伺服器」。
DoS 風險:
如果你同時開上萬個執行緒,等於對伺服器發動「小型 DDoS」。
對方可能會直接封鎖你。
程式安全:
使用 try-with-resources 確保 Socket 正確關閉。
避免無限併發造成「OutOfMemoryError」或「CPU 100%」。
限制併發:用 FixedThreadPool 控制同時執行的任務數量。
節流:在提交任務時加 Thread.sleep(...) 或計數器控制速度。
安全性考量:合法使用、避免 DoS、自身程式也要穩定。
package day1;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.;
import java.util.concurrent.;
import java.util.concurrent.atomic.AtomicInteger;
public class Day17demo {
public static void main(String[] args) throws InterruptedException {
String host = "127.0.0.1"; // 測試自己電腦
int start = 20, end = 40; // 掃描範圍
int timeout = 500; // 每個連線 timeout 時間 (毫秒)
int threads = 10; // 限制同時進行的執行緒數量
ExecutorService executorService = Executors.newFixedThreadPool(threads);
List<Future<String>> results = new ArrayList<>();
AtomicInteger count = new AtomicInteger(0);
// 提交任務(有節流機制)
for (int p = start; p <= end; p++) {
final int port = p;
Future<String> future = executorService.submit(() -> {
try (Socket socket = new Socket()) {
socket.connect(new InetSocketAddress(host, port), timeout);
count.incrementAndGet();
return String.format("Port %d -> OPEN", port);
} catch (IOException e) {
return String.format("Port %d -> CLOSED/TIMEOUT", port);
}
});
results.add(future);
// 🎯 節流:避免瞬間壓力過大
Thread.sleep(50);
}
// 關閉執行緒池並等待完成
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.MINUTES);
// 收集所有結果
List<String> output = new ArrayList<>();
for (Future<String> f : results) {
try {
output.add(f.get()); // 等待任務完成
} catch (ExecutionException e) {
output.add("執行錯誤: " + e.getMessage());
}
}
// 輸出所有結果
for (String line : output) {
System.out.println(line);
}
System.out.println("Open ports count = " + count.get());
}
}
今天學到的「限制併發」和「節流」讓我理解到,程式不只是「能跑」而已,更要考慮效率和安全性。
Thread.sleep(50):在提交任務之間休息 50ms,避免一次性丟太多任務(這就是「節流」)。
以前我以為多開執行緒越快越好,但現在知道如果不限制,反而可能拖垮電腦或讓伺服器崩潰。
就像排隊買票,如果所有人同時擠進去,反而誰也買不到。
另外,程式的安全性也提醒我,寫程式要有「工程師的責任感」,不能亂對別人伺服器測試。
今天不只是 Java 技術的進步,也讓我有了「資安工程師」的思維:程式設計要安全、要負責。