Coding Planet

๊ฒฝ์Ÿ์กฐ๊ฑด(Race Condition)๊ณผ Lock - ๊ฐœ๋…๊ณผ ์˜ˆ์‹œ ํ•œ๋ฒˆ์— ์ •๋ฆฌํ•˜๊ธฐ ๋ณธ๋ฌธ

๐Ÿ’ป Java Study/Java ์ด๋ก  ์ •๋ฆฌ

๊ฒฝ์Ÿ์กฐ๊ฑด(Race Condition)๊ณผ Lock - ๊ฐœ๋…๊ณผ ์˜ˆ์‹œ ํ•œ๋ฒˆ์— ์ •๋ฆฌํ•˜๊ธฐ

jhj.sharon 2024. 2. 29. 15:22
๋ฐ˜์‘ํ˜•
ConCurrentHashMap๊ณผ HashTable์„ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๊ฒฝ์Ÿ์กฐ๊ฑด๊ณผ Lock์ด ์ฃผ์š” ํ‚ค์›Œ๋“œ๋กœ ๋“ฑ์žฅํ–ˆ๋‹ค. ์Šค๋ ˆ๋“œ๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋“ฑ์žฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๊ฐœ๋… ๋ชจ๋‘ ์–ด๋ ดํ’‹์ด ์•Œ๊ณ  ์žˆ๊ธด ํ•˜์ง€๋งŒ ์ด๋ฒˆ ๊ธฐํšŒ์— ์ •๋ฆฌํ•˜๊ณ  ๊ฐ€์•ผ๊ฒ ๋‹ค.

 

 

| ๊ฒฝ์Ÿ ์กฐ๊ฑด (Race Condition)๊ณผ Lock

๊ฒฝ์Ÿ ์กฐ๊ฑด์€ ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๋‘ ๊ฐœ ์ด์ƒ์˜ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋‚˜ ์ž์›์— ๋™์‹œ์— ์ ‘๊ทผํ•˜๋ ค๊ณ  ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ์ด๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ์Šค๋ ˆ๋“œ๋“ค์˜ ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ํ”„๋กœ๊ทธ๋žจ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋Š” ๋ฐ์ดํ„ฐ์˜ ์ผ๊ด€์„ฑ๊ณผ ์ •ํ™•์„ฑ์„ ํ•ด์น  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฝ์Ÿ ์กฐ๊ฑด์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜(๋Œ€ํ‘œ์ ์œผ๋กœ Lock)์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šค๋ ˆ๋“œ๋“ค์ด ์ž์›์— ์•ˆ์ „ํ•˜๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•œ๋‹ค. 

 

Lock์€ ๋™์‹œ์— ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์ž์›์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๋Š” ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค. ์ž๋ฐ”์—์„œ๋Š” synchronized ํ‚ค์›Œ๋“œ ๋˜๋Š” Lock ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Lock์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค. synchronized๋Š” ๋ฉ”์„œ๋“œ๋‚˜ ์ฝ”๋“œ ๋ธ”๋ก ์ „์ฒด๋ฅผ ์ž„๊ณ„ ์˜์—ญ์œผ๋กœ ์„ ์ •ํ•˜๋ฉฐ, Lock ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋” ์„ธ๋ฐ€ํ•œ ๋™๊ธฐํ™” ์ œ์–ด๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•œ๋‹ค.

 

์˜ˆ์‹œ๋ฅผ ํ†ตํ•˜์—ฌ ๋™๊ธฐํ™” ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•  ๋•Œ์™€ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๋•Œ๋ฅผ ๋น„๊ตํ•˜์—ฌ ๊ฒฝ์Ÿ์กฐ๊ฑด๊ณผ Lock์„ ์„ค๋ช…ํ•˜๋ ค๊ณ  ํ•œ๋‹ค.

 

 

| ex1. Lock์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋‘ ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ™์€ ์นด์šดํ„ฐ ๊ฐ’์„ ๋™์‹œ์— ์ฆ๊ฐ€์‹œํ‚ค๋ ค๊ณ  ํ•  ๋•Œ ๊ฒฝ์Ÿ์กฐ๊ฑด์ด ๋ฐœ์ƒํ•˜๋Š” ์ƒํ™ฉ์„ ๋ณด์—ฌ์ค€๋‹ค.

public class Counter {
    private int count = 0;

    public void increment() {
        count++; // ์—ฌ๊ธฐ์— lock์ด ์—†์Œ
    }

    public int getCount() {
        return count;
    }

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

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

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

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final count: " + counter.getCount()); // ์˜ˆ์ƒ ๊ฒฐ๊ณผ๋Š” 2000์ด์ง€๋งŒ, ๊ฒฝ์Ÿ ์กฐ๊ฑด์œผ๋กœ ์ธํ•ด ๋‹ค๋ฅธ ๊ฐ’์ด ์ถœ๋ ฅ๋  ์ˆ˜ ์žˆ์Œ
    }
}

 

์‹ค์ œ ์ธํ…”๋ฆฌ์ œ์ด์—์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•œ ๊ฒฐ๊ณผ 1906์ด ๋‚˜์™”๋‹ค. increment() ๋ฉ”์„œ๋“œ์— lock์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ์Šค๋ ˆ๋“œ(t1, t2)๊ฐ€ count ๋ณ€์ˆ˜๋ฅผ ๋™์‹œ์— ์ฆ๊ฐ€์‹œํ‚ค๋ คํ•  ๋•Œ ์„œ๋กœ์˜ ์ž‘์—…์„ ๋ฐฉํ•ดํ•œ ๊ฒƒ์ด๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ count ๊ฐ’(0)์„ ์ฝ๊ณ  ๊ฐ’์„ ์ฆ๊ฐ€์‹œํ‚ค๊ธฐ ์ „์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋„ count(0)๋ฅผ ์ฝ์–ด ์ฆ๊ฐ€๊ฐ€ ํ•œ๋ฒˆ๋งŒ ์ด๋ฃจ์–ด ์งˆ ์ˆ˜ ์žˆ๋‹ค.  ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ 0์„ ์ฝ๊ณ  -> 1๋กœ ์ฆ๊ฐ€ -> ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ 1์„ ์ฝ๊ณ  -> 2๋กœ ์ฆ๊ฐ€ ์ด๋Ÿฌํ•œ ์ •์ƒ์ ์ธ ์ฆ๊ฐ€๊ฐ€ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๊ณ  ์ค‘๊ฐ„์— ๊ผฌ์ธ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฌํ•œ ๋ฌธ์ œ ๋•Œ๋ฌธ์— 2000์ด ๋‚˜์˜ค์ง€ ์•Š์•˜๋‹ค.

 

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

 

| ex2. Lock์„ ์‚ฌ์šฉํ•œ ๊ฒฝ์šฐ

์•„๋ž˜ ์ฝ”๋“œ๋Š” ์œ„ ์ฝ”๋“œ์— ๊ฒฝ์Ÿ์กฐ๊ฑด์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Lock์„ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ์ด๋‹ค.

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

public 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 static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

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

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

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Final count: " + counter.getCount()); // 2000์ด ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ๋จ
    }
}

 

์—ฌ๋Ÿฌ๋ฒˆ ์‹คํ–‰ํ–ˆ๋Š”๋ฐ ๋ชจ๋‘ 2000์ด ์ถœ๋ ฅ๋˜์—ˆ๋‹ค. ์ด ์˜ˆ์‹œ์—์„œ increment ๋ฉ”์„œ๋“œ๋Š” ReentrantLock์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™๊ธฐํ™”๋œ๋‹ค.

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

 

 

 

 

 

๋ฐ˜์‘ํ˜•
Comments