๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ์‚ฌ์šฉ ์‹œ ๊ผญ ์•Œ์•„์•ผ ํ•  ๋‚ด์šฉ - Thread-Safe

๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ๋Š” ํ”„๋กœ๊ทธ๋žจ์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์ด๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ž์›์„ ๊ณต์œ ํ•œ๋‹ค๋ฉด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฌธ์ œ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. 

 


Java์—์„  ์–ด๋–ป๊ฒŒ Thread-Safeํ•˜๊ฒŒ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›์„ ํ•˜๋Š”์ง€, ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•๋“ค์— ๋Œ€ํ•ด ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์•Œ์•„๋ณด๊ณ 

 

์‹ค์ œ ๊ฐœ๋ฐœ ์ค‘์ด๋˜ Thread-UnSafeํ•œ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค. 

 

 

1. Thread Safe (์“ฐ๋ ˆ๋“œ ์„ธ์ดํ”„) ๋ž€?

  • ๋ฉ€ํ‹ฐ ์“ฐ๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ, ์–ด๋–ค ๊ณต์œ  ์ž์›์— ์—ฌ๋Ÿฌ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ ‘๊ทผํ•ด๋„, ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰์— ๋ฌธ์ œ๊ฐ€ ์—†๋Š” ์ƒํƒœ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
  • Race Condition ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•ด๋„ ์‹คํ–‰์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ์ง€ ์•Š๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
    • Race Condition ์ด๋ž€?
    • ๊ฒฝ์Ÿ ์ƒํƒœ๋กœ, ๊ณต์œ  ์ž์›์— ๋Œ€ํ•ด ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ์ฝ๊ฑฐ๋‚˜ ์“ฐ๋Š” ์ƒํ™ฉ์„ ์˜๋ฏธํ•œ๋‹ค.

 

 

Thread Safe๋ฅผ ์ง€ํ‚ค๋Š” ๋ฐฉ๋ฒ•์œผ๋ก  ํฌ๊ฒŒ 4๊ฐ€์ง€๊ฐ€ ์žˆ๋‹ค. 

1. Re-entrancy(์žฌ์ง„์ž…์„ฑ)

  • ์ •์ (์ „์—ญ) ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.
  • ์ •์ (์ „์—ญ) ๋ณ€์ˆ˜์˜ ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.
  • ํ˜ธ์ถœ์ž๊ฐ€ ํ˜ธ์ถœ ์‹œ ์ œ๊ณตํ•œ ๋งค๊ฐœ ๋ณ€์ˆ˜๋งŒ์œผ๋กœ ๋™์ž‘ํ•ด์•ผ ํ•œ๋‹ค.
  • ์‹ฑ๊ธ€ํ†ค ๊ฐ์ฒด์˜ ์ž ๊ธˆ์— ์˜์กดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.
  • ๋‹ค๋ฅธ ๋น„-์žฌ์ง„์ž… ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์•ˆ ๋œ๋‹ค.

2. Thread-local Storage

  • ๊ณต์œ ์ž์›์˜ ์‚ฌ์šฉ์„ ์ตœ๋Œ€ํ•œ ์ค„์ด๊ณ  ๊ฐ๊ฐ์˜ Thread์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ €์žฅ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ ์ ‘๊ทผ์„ ๋ง‰๋Š” ๊ฒƒ

3. Mutual Exclusion

  • Lock์„ ํ†ตํ•ด ๊ณต์œ ์ž์›์„ ์ œ์–ดํ•˜๋Š” ๊ฒƒ
  • ๋ฎคํ…์Šค, ์„ธ๋งˆํฌ์–ด ๋“ฑ์„ ์ด์šฉํ•œ๋‹ค.

4. Atomic Operation Atomic

  • ์—ฐ์‚ฐ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ
  • ๋”์ด์ƒ ์ชผ๊ฐค ์ˆ˜ ์—†๋Š” ๊ฐ€์žฅ ์ž‘์€ ๋‹จ์œ„์˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ
  • ๋„์ค‘์— ์ธํ„ฐ๋ŸฝํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์—†๋‹ค.

 

 

 

 

 

2. Thread-Safe๊ฐ€ ์ง€์ผœ์ง€์ง€ ์•Š์„ ๋•Œ

์ฝ”๋“œ๋ฅผ Thread-Safeํ•˜๊ฒŒ ์ž‘์„ฑํ•˜์ง€ ์•Š์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€๋‹ค. 

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ตœ์†Œ 100๋ช…์˜ ์˜ˆ์•ฝ์ด ์ฐจ์•ผ์ง€ ๋ฒ„์Šค๋ฅผ ์šดํ–‰ํ•œ๋‹ค.
  • 100๋ช… ์ดํ•˜์ผ ๊ฒฝ์šฐ, ๊ฒฝ๊ณ  ๋ฉ”์„ธ์ง€์™€ ํ•จ๊ป˜ ํ˜„์žฌ ์˜ˆ์•ฝ ์ธ์›์ˆ˜๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. 
@Getter
public class Bus {
    private int minOccupancy = 100;
    private int reservation = 0;

    public void getBusTicket() {
        try {
            Thread.sleep(100);
            reservation++;
            if (reservation <= minOccupancy) {
                Thread.sleep(100);
                System.out.println("์ตœ์†Œ ์ธ์›์ธ 100๋ช…์„ ๋„˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์˜ˆ์•ฝ ์ธ์›: " + reservation);
            }
        } catch (InterruptedException e) {
            System.out.println("ERROR!");
        }
    }
}

 

 

 

๋งŒ์•ฝ 500๋ช…์ด ๋™์‹œ์— ๋ฒ„์Šค๋ฅผ ์˜ˆ๋งคํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž. 

100๋ช… ์ดํ•˜์ผ ๋•Œ๋Š” "์ตœ์†Œ ์ธ์›์ธ 100๋ช…์„ ๋„˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์˜ˆ์•ฝ ์ธ์›: x" ๋ผ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ์ถœ๋ ฅ๋˜์•ผ ํ•˜๊ณ ,

์ตœ์ข… ์˜ˆ์•ฝ ์ธ์›์€ 500๋ช…์ด์–ด์•ผ ํ•œ๋‹ค. 

class BusTest {
    private final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(100);

    @Test
    void ๋‹ค์ˆ˜์˜_์‚ฌ๋žŒ๋“ค์ด_๋™์‹œ์—_๋ฒ„์Šคํ‹ฐ์ผ“์„_๊ตฌ๋งคํ•œ๋‹ค() throws InterruptedException {

        int N = 500;
        CountDownLatch latch = new CountDownLatch(N);
        Bus bus = new Bus();

        for (int i = 0; i < N; i++) {
            THREAD_POOL.execute(() -> {
                bus.getBusTicket();
                latch.countDown();
            });
        }

        latch.await();

        int busReservation = bus.getReservation();
        System.out.println("======= Total reservation: " + busReservation + " =======");
        assertThat(busReservation).isNotEqualTo(N);
    }
}

 

@Test
void ๋‹ค์ˆ˜์˜_์‚ฌ๋žŒ๋“ค์ด_๋™์‹œ์—_๋ฒ„์Šคํ‹ฐ์ผ“์„_๊ตฌ๋งคํ•œ๋‹ค() throws InterruptedException {

    int N = 500;
    CountDownLatch latch = new CountDownLatch(N);
    Bus bus = new Bus();

    for (int i = 0; i < N; i++) {
        THREAD_POOL.execute(() -> {
            bus.getBusTicket();
            latch.countDown();
        });
    }

    latch.await();

    int busReservation = bus.getReservation();
    System.out.println("======= Total reservation: " + busReservation + " =======");
    assertThat(busReservation).isEqualTo(N);
}

 

ํ•˜์ง€๋งŒ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜์™”๋‹ค. 

 

 

 

1. 100๋ช… ์ด์ƒ์ผ ๋•Œ๋„ ๋ฌธ๊ตฌ๊ฐ€ ์ถœ๋ ฅ๋œ ์ด์œ 

1๋ฒˆ ์“ฐ๋ ˆ๋“œ๊ฐ€ if๋ฌธ์„ ํ†ต๊ณผํ•œ ์‹œ์ ์— 2๋ฒˆ ์“ฐ๋ ˆ๋“œ๊ฐ€ reservation ์˜ˆ์•ฝ ํ•„๋“œ์— ์ ‘๊ทผํ•ด ++ ์„ ์ˆ˜ํ–‰ํ–ˆ์„ ์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

 

 

2. ์ตœ์ข… ๊ตฌ๋งค ์ธ์›์ด 486๋ช…

ํ˜„์žฌ reservation ๊ฐ’์ด n ์ด๋ผ ๊ฐ€์ •ํ•˜๋ฉด ์“ฐ๋ ˆ๋“œ 1, 2 ๊ฐ€ ++์„ ํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ๋‹ค.

์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋‚˜๋ฉด

์“ฐ๋ ˆ๋“œ1: n+1

์“ฐ๋ ˆ๋“œ2: n+1
์˜ ๊ฐ’์œผ๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค. ๊ฒฐ๊ณผ์ ์œผ๋กœ n+2๊ฐ€ ์•„๋‹Œ n+1์ด ๋˜๋Š” ๊ฒƒ์ด๋‹ค. 

 

 

๊ฒฐ๊ตญ ์ด ๋‘ ๊ฐ€์ง€ ์ƒํ™ฉ ๋ชจ๋‘ Race Condition(๊ฒฝ์Ÿ ์ƒํƒœ)๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์œผ๋กœ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. Race Condition์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋™์‹œ์— ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ณต์œ  ์ž์›์— ์ ‘๊ทผํ•˜๊ณ  ์ด๋ฅผ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒํ•œ๋‹ค. 

์Šค๋ ˆ๋“œ ์Šค์ผ€์ฅด๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์— ์˜ํ•ด ์Šค๋ ˆ๋“œ๋Š” ์–ธ์ œ๋“  swap ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋Š ์‹œ์ ์— ์–ด๋–ค ์ˆœ์„œ๋กœ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ์ž์›์— ์ ‘๊ทผํ• ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค. 

 

๋”ฐ๋ผ์„œ, ์Šค๋ ˆ๋“œ ์Šค์ผ€์ฅด๋ง ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ๊ฒฐ์ •ํ•˜๋Š” ์Šค๋ ˆ๋“œ์˜ ์ ‘๊ทผ ์‹œ์ ๊ณผ ์ˆœ์„œ์— ๋”ฐ๋ผ ์‹คํ–‰ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋œ๋‹ค.

 

 

๊ทธ๋ ‡๋‹ค๋ฉด Java์—์„œ๋Š” ์–ด๋–ป๊ฒŒ Thread-Safeํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์„๊นŒ?

 

 

 

 

 

 

 

3. ํ•ด๊ฒฐ ๋ฐฉ์•ˆ

- Synchronized

์ž๋ฐ”์˜ ๋ชจ๋“  ๊ฐ์ฒด๋Š” ํ•˜๋‚˜์˜ Monitor๋ฅผ ๊ฐ–๊ณ  ์žˆ๋‹ค. Monitor๋Š” ์ƒํ˜ธ ๋ฐฐ์ œ(mutual exclusion)์™€ ํ˜‘๋ ฅ(cooperation) ๋‘ ๊ฐ€์ง€ ์ข…๋ฅ˜์˜ ์Šค๋ ˆ๋“œ ๋™๊ธฐํ™”๋ฅผ ์ง€์›ํ•œ๋‹ค.

์ƒํ˜ธ ๋ฐฐ์ œ๋Š” Lock์„ ํ†ตํ•ด ๋‹ค์ˆ˜์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ณต์œ  ์ž์›์— ๋Œ€ํ•ด ๋…๋ฆฝ์ ์œผ๋กœ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋ฉฐ,

ํ˜‘๋ ฅ์€ wait์™€ notify ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ์Šค๋ ˆ๋“œ๊ฐ€ ์ƒํ˜ธ ํ˜‘๋ ฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

์˜ˆ๋ฅผ ๋“ค์–ด ๋ฒ„ํผ๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์˜ค๋Š” ์Šค๋ ˆ๋“œ์™€ ๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ, ๋ฒ„ํผ๊ฐ€ ๋น„์–ด ์žˆ๋‹ค๋ฉด ๋ฒ„ํผ์— ๋ฐ์ดํ„ฐ๋ฅผ ์“ฐ๋Š” ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฑ„์šธ ๋•Œ๊นŒ์ง€ waitํ•˜๊ณ , ํ•ด๋‹น ์ž‘์—…์ด ์ข…๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ์ฝ๊ธฐ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

 

 

 

์ž๋ฐ”์˜ synchrnoized ํ‚ค์›Œ๋“œ๋Š” ์Šค๋ ˆ๋“œ ๊ฐ„ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๊ธฐ๋ฒ•์ด๋‹ค.

synchronized ํ‚ค์›Œ๋“œ๋Š” ๊ฐ์ฒด ์•ˆ์— ์กด์žฌํ•˜๋Š” Monitor๋ฅผ ์ด์šฉํ•ด ๋™๊ธฐํ™”๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๊ฐ€ synchronized๋กœ ์ง€์ •ํ•œ ์ž„๊ณ„ ์˜์—ญ์— ๋“ค์–ด๊ฐ€ ์žˆ์„ ๋•Œ Lock์ด ๊ฑธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž„๊ณ„ ์˜์—ญ์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. ์ดํ›„ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๊ฐ€ ์ž„๊ณ„ ์˜์—ญ์˜ ์ฝ”๋“œ๋ฅผ ๋ชจ๋‘ ์‹คํ–‰ํ•œ ๋’ค ๋ฒ—์–ด๋‚˜๋ฉด unlock ์ƒํƒœ๊ฐ€ ๋˜์–ด ๋Œ€๊ธฐํ•˜๊ณ  ์žˆ๋˜ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์ด ์ž„๊ณ„์˜์—ญ์— ์ ‘๊ทผํ•ด ๋‹ค์‹œ Lock์„ ๊ฑธ์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

 

์•ž์„œ ์ง„ํ–‰ํ–ˆ๋˜ ํ…Œ์ŠคํŠธ๋ฅผ synchronized๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋‹ค์‹œ ์ง„ํ–‰ํ•ด๋ณด์•˜๋‹ค. 

 

@Getter
public class Bus {
    private int minOccupancy = 100;
    private int reservation = 0;

    public synchronized void getBusTicket() {
        try {
            Thread.sleep(100);
            reservation++;
            if (reservation <= minOccupancy) {
                Thread.sleep(100);
                System.out.println("์ตœ์†Œ ์ธ์›์ธ 100๋ช…์„ ๋„˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์˜ˆ์•ฝ ์ธ์›: " + reservation);
            }
        } catch (InterruptedException e) {
            System.out.println("ERROR!");
        }
    }
}

 

@Test
void ๋‹ค์ˆ˜์˜_์‚ฌ๋žŒ๋“ค์ด_๋™์‹œ์—_๋ฒ„์Šคํ‹ฐ์ผ“์„_๊ตฌ๋งคํ•œ๋‹ค() throws InterruptedException {

    int N = 500;
    CountDownLatch latch = new CountDownLatch(N);
    Bus bus = new Bus();

    for (int i = 0; i < N; i++) {
        THREAD_POOL.execute(() -> {
            bus.getBusTicket();
            latch.countDown();
        });
    }

    latch.await();

    int busReservation = bus.getReservation();
    System.out.println("======= Total reservation: " + busReservation + " =======");
    assertThat(busReservation).isEqualTo(N);
}

 

๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์ฒ˜๋Ÿผ ๋‚˜์˜จ๋‹ค. 

 

synchronized๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ Thread-Safeํ•œ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ ๋Š๋ฆฌ๋‹ค. ์ž„๊ณ„์˜์—ญ์˜ ์ฝ”๋“œ๋Š” ํ•˜๋‚˜์˜ ์“ฐ๋ ˆ๋“œ์”ฉ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

 

 

 

 

 

- Atomic Objects

์ž๋ฐ”๋Š” AtomicInteger, AtomicLong, AtomicBoolean ๋“ฑ์˜ atomic ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค. 

atomic ํด๋ž˜์Šค๋Š” ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์›์ž์„ฑ์„ ๋ณด์žฅํ•ด์ค€๋‹ค. ๋˜ํ•œ, ์•ž์—์„œ ์‚ดํŽด๋ณธ synchronized์™€๋Š” ๋‹ค๋ฅด๊ฒŒ blocking์ด ์•„๋‹Œ non-blocking ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๋ฉฐ ํ•ต์‹ฌ ๋™์ž‘ ์›๋ฆฌ๋Š” CAS(Compare And Swap) ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋‹ค.

 

@Getter
public class Bus {
    private int minOccupancy = 100;
    private AtomicInteger reservation = new AtomicInteger();

    public synchronized void getBusTicket() {
        try {
            Thread.sleep(100);
            int newReservation = reservation.incrementAndGet();
            if (newReservation <= minOccupancy) {
                Thread.sleep(100);
                System.out.println("์ตœ์†Œ ์ธ์›์ธ 100๋ช…์„ ๋„˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ์˜ˆ์•ฝ ์ธ์›: " + reservation);
            }
        } catch (InterruptedException e) {
            System.out.println("ERROR!");
        }
    }
}

 

 

์œ„์—์„œ ๋ดค๋˜ synchronized๋ž‘ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ˆ˜ํ–‰ ์‹œ๊ฐ„์„ ๋‚จ๊ฒผ๋‹ค. 

@Test
void ๋‹ค์ˆ˜์˜_์‚ฌ๋žŒ๋“ค์ด_๋™์‹œ์—_๋ฒ„์Šคํ‹ฐ์ผ“์„_๊ตฌ๋งคํ•œ๋‹ค() throws InterruptedException {
    long startTime = System.currentTimeMillis();


    int N = 500;
    CountDownLatch latch = new CountDownLatch(N);
    Bus bus = new Bus();

    for (int i = 0; i < N; i++) {
        THREAD_POOL.execute(() -> {
            bus.getBusTicket();
            latch.countDown();
        });
    }

    latch.await();

    int busReservation = bus.getReservation().get();
    System.out.println("======= Total reservation: " + busReservation + " =======");
    long endTime = System.currentTimeMillis();
    System.out.println("์ด ๊ฑธ๋ฆฐ ์‹œ๊ฐ„: " + (endTime - startTime) + " milliseconds.");
    assertThat(busReservation).isEqualTo(N);
}

 

 

 

์›ํ•˜๋Š” ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™”๋‹ค. 

Atomic์ด ์–ด๋–ค ์—ญํ• ์„ ํ•ด์คฌ๊ธธ๋ž˜ Thread-Safeํ•œ ์ฝ”๋“œ๊ฐ€ ๋์„๊นŒ??

 

์ž๋ฐ”๋กœ ์ž‘์„ฑ๋œ ํ•œ ์ค„์˜ ์—ฐ์‚ฐ์ธ reservation++์€ ๋ณ€์ˆ˜์˜ ๊ฐ’์„ ์ฝ๊ณ , ์—ฐ์‚ฐํ•œ ๋’ค ์ €์žฅํ•˜๋Š” ์„ธ ์ค„์˜ ๊ธฐ๊ณ„์–ด๋กœ ์ชผ๊ฐœ์งˆ ์ˆ˜ ์žˆ๊ณ , ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๊ธฐ๊ณ„์–ด ์—ฐ์‚ฐ๋“ค์ด ํ•œ ๋ฒˆ์— ๋ชจ๋‘ ์‹คํ–‰๋ ๊ฑฐ๋ผ๋Š” ๋ณด์žฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๊ฐ’์„ ์ฝ๊ณ  ์—ฐ์‚ฐํ•˜๋Š” ๊ธฐ๊ณ„์–ด๊นŒ์ง€ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋กœ swap ๋˜๋Š” ์ƒํ™ฉ์„ ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

 

atomic ํด๋ž˜์Šค๋Š” ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ์•ˆ์ „ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋ฌด์—‡๋ณด๋‹ค ์ˆ˜ํ–‰ ์‹œ๊ฐ„์ด 667ms๋กœ ๊ต‰์žฅํžˆ ๋นจ๋ž๋‹ค. 

 

synchronized ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋‹ˆ.. ๋ฌด๋ ค 64031ms ๊ฐ€ ๊ฑธ๋ ธ๋‹ค. 

 

ํ•˜๋‚˜์˜ ํ•„๋“œ๋ฅผ ๊ณต์œ  ์ž์›์œผ๋กœ ๊ฐ–๋Š”๋‹ค๋ฉด Atomic ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„๋ณด์˜€๋‹ค. ํ•˜์ง€๋งŒ ํ•„๋“œ๊ฐ€ ์•„๋‹Œ ๋กœ์ง์ด Thread-Safeํ•ด์•ผ ํ•œ๋‹ค๋ฉด ๋Š๋ฆฌ์ง€๋งŒ synchronized๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐ์ด ๋“ ๋‹ค.