run() 을 직접 호출Thread t = new Thread(() -> heavyWork());
t.run(); // 새 스레드가 아니라 현재 스레드에서 그냥 실행됨run() 은 평범한 메서드입니다. 병렬성이 전혀 생기지 않는데 코드는 멀쩡해 보여서 오래 눈치 못 챕니다.
✅
Thread t = new Thread(() -> heavyWork());
t.start(); // OS 스레드 생성 → 그 스레드가 run() 호출volatile 로 count++ 보호private volatile int count;
public void hit() { count++; } // 여전히 갱신 손실 발생volatile 은 가시성만 보장합니다. 읽기-더하기-쓰기 세 단계 사이에 다른 스레드가 끼어드는 것은 막지 못합니다.
✅
private final AtomicInteger count = new AtomicInteger();
public void hit() { count.incrementAndGet(); }
// 또는 synchronized 블록submit() 하고 Future.get() 을 안 함 → 예외 실종pool.submit(() -> { processPayment(order); }); // 안에서 예외가 나도 아무 로그도 없음submit 은 예외를 Future 안에 담아 두고, 아무도 get() 을 안 하면 그대로 사라집니다. 결제가 실패했는데 성공한 줄 아는 사고가 납니다.
✅
Future<?> f = pool.submit(() -> processPayment(order));
try { f.get(); } catch (ExecutionException e) { log.error("payment failed", e.getCause()); }
// 결과가 필요 없으면 execute() 를 쓰고 UncaughtExceptionHandler 를 설정하거나, 작업 안에서 try-catchshutdown() 누락 → JVM 이 안 끝남ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(task);
// main 이 끝나도 워커 스레드(non-daemon)가 살아 있어 프로세스가 종료되지 않음✅
try (ExecutorService pool = Executors.newFixedThreadPool(4)) { // JDK 19+
pool.submit(task);
} // close() → shutdown() + awaitTermination()
// 구버전: finally { pool.shutdown(); pool.awaitTermination(30, SECONDS); }synchronized void transferTo(Account other, long amt) {
synchronized (other) { ... } // this → other 순서. 반대 방향 호출과 만나면 데드락
}락을 잡은 채 다른 락을 잡으면 순환 대기 조건이 성립할 수 있습니다.
✅
// 전역 순서(예: id) 로 정렬해서 항상 같은 순서로 잠근다 (예제 2 참고)
// 또는 tryLock(timeout) 으로 실패 시 전부 해제 후 재시도remove() 안 함CURRENT_USER.set(user);
handle(request);
// remove 없음 → 이 워커 스레드가 다음 요청을 처리할 때 이전 사용자가 보임✅
CURRENT_USER.set(user);
try { handle(request); }
finally { CURRENT_USER.remove(); }ExecutorService pool = Executors.newFixedThreadPool(200, Thread.ofVirtual().factory()); // 의미 없음가상 스레드는 싸니까 작업마다 하나씩 만드는 것이 설계 의도입니다. 200개로 제한하면 이점이 사라집니다. 동시성 상한이 필요하면 Semaphore 로 제어합니다.
✅
try (var ex = Executors.newVirtualThreadPerTaskExecutor()) {
Semaphore limit = new Semaphore(50); // 외부 API 동시 호출 50개 제한
for (var req : requests) ex.submit(() -> { limit.acquire(); try { call(req); } finally { limit.release(); } });
}