원자적 연산 

  • long과 double을 제외한 원시 타입 값 할당(읽고, 쓰기)
    • long과 double은 내부적으로 32비트씩 잘라서 연산을 진행하므로 원자적 연산이 아니다.
    • 이는 volatile 키워드를 변수 선언 시에 붙여줌으로써 해결할 수 있다.
public class AtomicOperations {
    public static void main(String[] args) {
        Metrics metrics = new Metrics();

        BusinessLogic businessLogicThread1 = new BusinessLogic(metrics);
        BusinessLogic businessLogicThread2 = new BusinessLogic(metrics);

        MetricsPrinter metricsPrinter = new MetricsPrinter(metrics);

        businessLogicThread1.start();
        businessLogicThread2.start();
        metricsPrinter.start();
    }


    public static class MetricsPrinter extends Thread{
        private Metrics metrics;

        public MetricsPrinter(Metrics metrics) {
            this.metrics = metrics;
        }

        @Override
        public void run() {
            while(true){
                try{
                    Thread.sleep(100);
                }catch (InterruptedException e){
                }
                double currentAverage = metrics.getAverage();
                System.out.println("Current Average is "+currentAverage);
            }
        }
    }

    public static class BusinessLogic extends Thread {
        private Metrics metrics;
        private Random random = new Random();

        public BusinessLogic(Metrics metrics) {
            this.metrics = metrics;
        }

        @Override
        public void run() {
            while(true){
                long start = System.currentTimeMillis();
                try{
                    Thread.sleep(random.nextInt(10));
                }catch (InterruptedException e){
                }
                long end = System.currentTimeMillis();

                metrics.addSample(end-start);
            }
        }
    }

    public static class Metrics {
        private long count = 0;
        private volatile double average = 0.0;
		//메소드 내용 자체가 원자적 연산이 아니므로 synchronized 붙여준다.
        public synchronized void addSample(long sample){
            double currentSum = average * count;
            count++;
            average = (currentSum + sample)/count;
        }
		//double이므로 volatile을 붙여 원자적 연산이 진행되도록 한다.
        public double getAverage() {
            return average;
        }
    }
}

 

Deadlock

public static void main(String[] args) {
    Intersection intersection = new Intersection();
    Thread trainAThread = new Thread(new TrainA(intersection));
    Thread trainBThread = new Thread(new TrainB(intersection));

    trainAThread.start();
    trainBThread.start();

}

public static class TrainA implements Runnable {

    private Intersection intersection;
    private Random random = new Random();

    public TrainA(Intersection intersection) {
        this.intersection = intersection;
    }

    @Override
    public void run() {
        while (true) {
            long sleepingTime = random.nextInt(5);
            try {
                Thread.sleep(sleepingTime);
            } catch (InterruptedException e) {
            }
            intersection.takeRoadA();
        }
    }
}

public static class TrainB implements Runnable {

    private Intersection intersection;
    private Random random = new Random();

    public TrainB(Intersection intersection) {
        this.intersection = intersection;
    }

    @Override
    public void run() {
        while (true) {
            long sleepingTime = random.nextInt(5);
            try {
                Thread.sleep(sleepingTime);
            } catch (InterruptedException e) {
            }
            intersection.takeRoadB();
        }
    }
}

public static class Intersection {
    private Object roadA = new Object();
    private Object roadB = new Object();

    public void takeRoadA() {
        synchronized (roadA) {
            System.out.println("Road A is locked by thread " + Thread.currentThread().getName());
            synchronized (roadB) {
                System.out.println("Train is passing through road A");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public void takeRoadB() {
        synchronized (roadB) {
            System.out.println("Road B is locked by thread " + Thread.currentThread().getName());
            synchronized (roadA) {
                System.out.println("Train is passing through road B");
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

 

데드락 해결법 

  • 가장 간단한 방법은 순환 대기를 예방하는 것으로, 동일한 순서로 공유 리소스를 잠그고, 모든 코드에 해당 순서를 유지하면 된다.
  • 아래와 같이 하면 데드락이 발생하지 않는다. 이때 언락의 순서는 상관없다.
public void takeRoadA() {
    synchronized (roadA) {
        System.out.println("Road A is locked by thread " + Thread.currentThread().getName());
        synchronized (roadB) {
            System.out.println("Train is passing through road A");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
        }
    }
}

public void takeRoadB() {
    synchronized (roadA) {
        System.out.println("Road B is locked by thread " + Thread.currentThread().getName());
        synchronized (roadB) {
            System.out.println("Train is passing through road B");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
        }
    }
}

'JAVA > 멀티스레딩' 카테고리의 다른 글

스레드간 통신  (0) 2024.08.30
락킹 심화  (0) 2024.08.29
멀티 스레드 주의 사항(병행성 문제)  (0) 2024.08.29
Thread Pooling  (0) 2024.08.28
스레드 조정  (0) 2024.08.28

+ Recent posts