Item 78. Mutable Data Sharing

๊ณต์œ  ์ค‘์ธ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋Š” ๋™๊ธฐํ™”ํ•ด ์‚ฌ์šฉํ•˜๋ผ

synchronized ํ‚ค์›Œ๋“œ๋Š” ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋‚˜ ๋ธ”๋ก์„ ํ•œ ๋ฒˆ์— ํ•œ ์Šค๋ ˆ๋“œ์”ฉ ์ˆ˜ํ–‰ํ•˜๋„๋ก ๋ณด์žฅํ•˜๋ฉฐ, ์•„๋ž˜์˜ ํŠน์ง•์„ ๊ฐ€์ง„๋‹ค.

  • ํ•œ ๊ฐ์ฒด๊ฐ€ ์ผ๊ด€๋œ ์ƒํƒœ๋ฅผ ๊ฐ€์ง€๊ณ  ์ƒ์„ฑ๋˜์—ˆ์„ ๋•Œ, ํ•ด๋‹น ๊ฐ์ฒด์— ์ ‘๊ทผํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ๊ฐ์ฒด์ด Lock์„ ํš๋“

  • Lock์„ ํš๋“ํ•œ ๋ฉ”์„œ๋“œ๋Š” ๊ฐ์ฒด์˜ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Œ

  • ๋ฉ”์„œ๋“œ ์‹คํ–‰์ด ์ข…๋ฃŒ๋˜๋ฉด Lock์„ ํ•ด์ œ

  • ๋™๊ธฐํ™” ์—†์ด๋Š” ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ์ฒด์˜ ์ผ๊ด€์„ฑ์ด ๊นจ์ง„ ์ƒํƒœ๋ฅผ ๋ณด๊ฒŒ ๋  ์ˆ˜ ์žˆ์Œ

    • ๋™๊ธฐํ™”๋Š” ๋ฉ”์„œ๋“œ๋‚˜ ๋ธ”๋ก์— ๋“ค์–ด๊ฐ„ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ์ฒด์˜ ์ผ๊ด€์„ฑ์ด ๊นจ์ง„ ์ƒํƒœ๋ฅผ ๋ณด์ง€ ๋ชปํ•˜๋„๋ก ๋ณด์žฅ

Java์—์„œ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ ์Šค๋ ˆ๋“œ๊ฐ„ ํ†ต์‹ 

์•„๋ž˜ ์ฝ”๋“œ๋Š” boolean ํ•„๋“œ๋ฅผ ๊ฒ€์‚ฌํ•˜๋ฉด์„œ ๊ทธ ๊ฐ’์ด true๊ฐ€ ์•„๋‹ˆ๋ฉด ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋Œ๋ฉฐ ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ์ฝ”๋“œ์ด๋‹ค.

class StopThread {

    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {

        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested) {
                i++;
                System.out.println(i);
            }
        });

        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

1์ดˆ ํ›„ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ stopRequested ํ•„๋“œ์˜ ๊ฐ’์„ true๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์‹ค์ œ๋ก  ๋ณ€๊ฒฝ๋œ ๊ฐ’์„ ๋ฐ”๋กœ ์ฝ์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค. ** ์‹คํ–‰ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ 1์ดˆ ํ›„์— ๋ฐ”๋กœ ์ข…๋ฃŒ๋  ์ˆ˜๋„ ์žˆ๋‹ค.

synchronized ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•œ ๋™๊ธฐํ™”

ํ•ด๋‹น ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„  stopRequested ํ•„๋“œ์— ์ ‘๊ทผํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋™๊ธฐํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

class StopThread {

    private static boolean stopRequested;

    private static synchronized void requestStop() {
        stopRequested = true;
    }

    private static synchronized boolean stopRequested() {
        return stopRequested;
    }

    public static void main(String[] args) throws InterruptedException {

        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested()) {
                i++;
            }
        });

        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        requestStop();
    }
}

์“ฐ๊ธฐ(requestStop)์™€ ์ฝ๊ธฐ(stopRequested) ๋ฉ”์„œ๋“œ ๋ชจ๋‘ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”๋ฐ, ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ ์ „๋ถ€ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์œผ๋ฉด ๋™๊ธฐํ™”๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š๋Š”๋‹ค.(๊ฐ„ํ˜น ์ž˜ ๋™์ž‘ํ•˜๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ์‹ค์ œ๋ก  ๊ทธ๋ ‡์ง€ ์•Š๋‹ค.)

volatile ํ•„๋“œ๋ฅผ ์ด์šฉํ•œ ๋™๊ธฐํ™”

volatile ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋™์ž‘์ด ํ•ญ์ƒ ๋ฉ”์ธ ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ˜์˜๋˜๋„๋ก ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

class StopThread {

    private static volatile boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {

        Thread backgroundThread = new Thread(() -> {
            int i = 0;
            while (!stopRequested) {
                i++;
            }
        });

        backgroundThread.start();

        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

volatile ์ฃผ์˜์‚ฌํ•ญ

์œ„์˜ ๋ฌธ์ œ๋Š” volatile ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ–ˆ์ง€๋งŒ, ํ•ด๋‹น ํ‚ค์›Œ๋“œ๋Š” ํ•„๋“œ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ํ†ต์‹ ๋งŒ ๋ณด์žฅํ•˜๋ฉฐ, ๋™์‹œ์„ฑ์„ ๋ณด์žฅํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

class AddTest {

    private static volatile int count = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                count++;
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                count++;
            }
        });

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

        thread1.join();
        thread2.join();

        System.out.println(count); // 1592872
    }
}

์œ„ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด 2000000์ด ๋‚˜์™€์•ผ ํ•  ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์‹ค์ œ๋กœ๋Š” 1592872(2000000์ด ์•„๋‹Œ ๊ฐ’)๊ฐ€ ๋‚˜์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” volatile ํ‚ค์›Œ๋“œ๋Š” ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ํ†ต์‹ ๋งŒ ๋ณด์žฅํ•˜๋ฉฐ, ๋™์‹œ์„ฑ์„ ๋ณด์žฅํ•˜์ง€๋Š” ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

synchronized ๋ธ”๋ก์„ ์ด์šฉํ•œ ๋™๊ธฐํ™”

์ฒ˜์Œ ๋‚˜์™”๋˜ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๋‹น ํ•„๋“œ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ํ†ต์‹ ๊ณผ ๋™์‹œ์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

class AddTest {

    private static int count = 0;

    private static synchronized void add() {
        count++;
    }

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                add();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                add();
            }
        });

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

        thread1.join();
        thread2.join();

        System.out.println(count);
    }
}

Atomic ํด๋ž˜์Šค๋ฅผ ์ด์šฉํ•œ ๋™๊ธฐํ™”

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๋™๊ธฐํ™” ๋ฌธ์ œ๋ฅผ ๋ณ„๋„์˜ synchronized ํ‚ค์›Œ๋“œ ์—†์ด ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ java.util.concurrent.atomic ํŒจํ‚ค์ง€์— ์žˆ๋Š” Atomic ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค. (๋‚ด๋ถ€์ ์œผ๋กœ volatile ํ‚ค์›Œ๋“œ์™€ CAS ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์‹œ์„ฑ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ์žˆ๋‹ค.)

import java.util.concurrent.atomic.AtomicInteger;

class AddTest {

    private static final AtomicInteger count = new AtomicInteger(0);


    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                count.incrementAndGet();
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000000; i++) {
                count.incrementAndGet();
            }
        });

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

        thread1.join();
        thread2.join();

        System.out.println(count.get());
    }
}

๊ฒฐ๋ก 

๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋‹ค๋ฃจ์—ˆ์ง€๋งŒ, ๋” ๋ณต์žกํ•œ ๋กœ์ง์—์„œ๋Š” ๋ฌธ์ œ๊ฐ€ ์–ด๋””์„œ ๋ฐœ์ƒํ• ์ง€ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹๋‹ค. ๋งŒ์•ฝ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด ์•„๋ž˜ ์‚ฌํ•ญ์„ ์ฃผ์˜ํ•˜์ž.

  • ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ์“ฐ๋Š” ๋ฉ”์„œ๋“œ ์ „๋ถ€ synchronized ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋™๊ธฐํ™”

  • volatile ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ†ต์‹ ์€ ๋ณด์žฅ๋˜์ง€๋งŒ, ๋™์‹œ์„ฑ์€ ๋ณด์žฅ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ์ฃผ์˜

  • ๊ฐ€๋ณ€ ๋ฐ์ดํ„ฐ๊ฐ€ java.util.concurrent.atomic ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณตํ•œ๋‹ค๋ฉด ํ•ด๋‹น ํด๋ž˜์Šค๋ฅผ ๊ณ ๋ คํ•ด๋„ ์ข‹์Œ

Last updated

Was this helpful?