본문 바로가기

명사 美 비격식 (무리 중에서) 아주 뛰어난[눈에 띄는] 사람[것]

JAVA

스레드의 동기화 lock클래스: ReentrantLock, ReentrantReadWriteLock, StampedLock

앞서 synchronized 블럭 동기화를 알아볼때 lock패키지에 대한 언급을 했었다.

https://standout.tistory.com/1428

 

synchronized 를 이용한 스레드의 동기화

스레드의 동기화란. 한 스레드가 특정작업을 끝마치기 전까지 다른 스레드에 의해 방해받지 않도록 하는것. 자바에서는 synchronized블럭을 이용해서 스레드의 동기화를 지원했지만 jsk1.5부터 java.u

standout.tistory.com

 

 

lock클래스의 종류는 3가지가 있다.

ReentrantLock

ReentrantReadWriteLock
StampedLock

 

 

ReentrantLock
`ReentrantLock`을 사용하여 공유 자원에 대한 접근을 동기화한다.

`increment()` 메서드에서 락을 획득하고 해제한다.

예시는 두 개의 스레드가 카운터를 증가시키는 작업을 동시에 수행하고, 최종 카운터 값이 올바르게 출력되는지 확인한다.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}

public class ReentrantLockExample {
    public static void main(String[] args) {
        Counter counter = new Counter();

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Counter value: " + counter.getCount());
    }
}

출력결과: Counter value: 2000

 

 

 

 

ReentrantReadWriteLock
`ReentrantReadWriteLock`을 사용하여 공유 자원에 대한 읽기와 쓰기를 분리하여 동기화한다.

예시는 작성자가 데이터를 쓰고, 독자가 데이터를 읽는 상황을 시뮬레이션한다.

import java.util.concurrent.locks.ReentrantReadWriteLock;

class SharedResource {
    private int data = 0;
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void writeData(int newData) {
        lock.writeLock().lock();
        try {
            data = newData;
        } finally {
            lock.writeLock().unlock();
        }
    }

    public int readData() {
        lock.readLock().lock();
        try {
            return data;
        } finally {
            lock.readLock().unlock();
        }
    }
}

public class ReentrantReadWriteLockExample {
    public static void main(String[] args) {
        SharedResource sharedResource = new SharedResource();

        Thread writerThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                sharedResource.writeData(i);
                System.out.println("Writer wrote: " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread readerThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                int data = sharedResource.readData();
                System.out.println("Reader read: " + data);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        writerThread.start();
        readerThread.start();
    }
}

출력결과:
Writer wrote: 0
Writer wrote: 1
Reader read: 1
Reader read: 1
Writer wrote: 2
Reader read: 2
Reader read: 2
Writer wrote: 3
Reader read: 3
Reader read: 3
Writer wrote: 4
Reader read: 4
Reader read: 4

 

 

StampedLock
`StampedLock`을 사용하여 공유 자원에 대한 읽기 작업과 쓰기 작업을 분리하여 동기화한다.

예시는 한 스레드가 위치를 이동시키고,

다른 스레드가 현재 위치에서 원점까지의 거리를 계산하는 상황을 시뮬레이션한다.

import java.util.concurrent.locks.StampedLock;

class Point {
    private double x, y;
    private final StampedLock lock = new StampedLock();

    void move(double deltaX, double deltaY) {
        long stamp = lock.writeLock();
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();
        double currentX = x, currentY = y;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

public class StampedLockExample {
    public static void main(String[] args) {
        Point point = new Point();

        Thread writerThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                point.move(i, i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread readerThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                double distance = point.distanceFromOrigin();
                System.out.println("Distance from origin: " + distance);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        writerThread.start();
        readerThread.start();
    }
}


출력결과:
Distance from origin: 0.0
Distance from origin: 1.4142135623730951
Distance from origin: 2.8284271247461903
Distance from origin: 4.242640687119285
Distance from origin: 5.656854249492381