1. 데드락과 레이스 컨디션: Java에서 피해야 할 동시성의 함정


  1. 데드락 (Deadlock)
public class DeadlockExample {
    private final Object lock1 = new Object();  // 첫 번째 락 객체
    private final Object lock2 = new Object();  // 두 번째 락 객체

    // 첫 번째 작업 메서드
    public void operation1() {
        synchronized (lock1) {  // 첫 번째 락을 획득
            System.out.println("Operation 1: Acquired lock1...");
            
            // ... (여기서 어떤 작업을 수행)
            
            synchronized (lock2) {  // 두 번째 락을 획득
                System.out.println("Operation 1: Acquired lock2...");
                
                // ... (여기서 어떤 작업을 수행)
            }
        }
    }

    // 두 번째 작업 메서드
    public void operation2() {
        synchronized (lock2) {  // 두 번째 락을 획득
            System.out.println("Operation 2: Acquired lock2...");
            
            // ... (여기서 어떤 작업을 수행)
            
            synchronized (lock1) {  // 첫 번째 락을 획득
                System.out.println("Operation 2: Acquired lock1...");
                
                // ... (여기서 어떤 작업을 수행)
            }
        }
    }

    // 메인에서 실행한다.
    public static void main(String[] args) {
        DeadlockExample example = new DeadlockExample();

        Thread t1 = new Thread(() -> example.operation1());
        Thread t2 = new Thread(() -> example.operation2());

        t1.start();
        t2.start();
    }
}
  1. 레이스 컨디션 (Race Condition)
public class RaceConditionExample {
    // 공유 자원
    private int sharedResource = 0;

    // 공유 자원을 1 증가시키는 메서드
    public void increment() {
        int temp = sharedResource;  // 현재 공유 자원의 값을 임시 변수에 저장
        temp = temp + 1;  // 임시 변수 값을 1 증가
        sharedResource = temp;  // 증가된 값을 다시 공유 자원에 저장
    }

    public static void main(String[] args) {
        RaceConditionExample example = new RaceConditionExample();  // 클래스 인스턴스 생성

        // 첫 번째 스레드. 1000번 공유 자원을 증가시킴
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        // 두 번째 스레드. 1000번 공유 자원을 증가시킴
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });

        t1.start();  // 첫 번째 스레드 시작
        t2.start();  // 두 번째 스레드 시작

        try {
            t1.join();  // 첫 번째 스레드가 종료될 때까지 대기
            t2.join();  // 두 번째 스레드가 종료될 때까지 대기
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 최종 공유 자원의 값 출력
        System.out.println("Final value of sharedResource: " + example.sharedResource);
    }
}

2. Java에서의 고급 동시성 패턴과 최적화 전략