JiwonDev

์ž๋ฐ”์˜ ๋™์‹œ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋™์ž‘๊ตฌ์กฐ (Atomic, Adder, Accumulator)

by JiwonDev

์ด์ „ ๊ธ€์—์„œ ์ž๋ฐ”์˜ Synchroized ํ‚ค์›Œ๋“œ๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์œผ๋‹ˆ, ๋™์‹œ์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ํ–ˆ์—ˆ๋‹ค.

 

Java์—์„œ์˜ ์ž„๊ณ„์˜์—ญ๊ณผ ๋ฐ๋“œ๋ฝ ํ•ด๊ฒฐ (sync, ๋ชจ๋‹ˆํ„ฐ)

 ์—ฌ๋Ÿฌ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํŠน์ • ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•  ๋•Œ, ๊ฐ ํ”„๋กœ์„ธ์Šค์—์„œ ๊ณต์œ  ๋ฐ์ดํ„ฐ๋ฅผ ์ ‘๊ทผ(access)ํ•˜๋Š” ์ฝ”๋“œ๋‚˜ ๋™์ž‘ ๋ถ€๋ถ„์„ ์ž„๊ณ„์˜์—ญ(Critical Section)์ด๋ผ๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ๊ณต์œ ๋œ ์ž์›์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋กœ์„ธ์„œ๊ฐ€

jiwondev.tistory.com

 

 

์“ฐ๋ ˆ๋“œ ๋™๊ธฐํ™” (sync, volatile, AtomicClass)

์“ฐ๋ ˆ๋“œ์˜ ๋™๊ธฐํ™” : ํ•œ ์“ฐ๋ ˆ๋“œ๊ฐ€ ์ง„ํ–‰์ค‘์ธ ์ž‘์—…์„ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๊ฐ€ ๊ฐ„์„œํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ. ์“ฐ๋ ˆ๋“œ๋Š” class์˜ ๋ฉค๋ฒ„ ๋ณ€์ˆ˜(์ž์›)์„ ์‚ฌ์šฉํ•œ๋‹ค. ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” ์“ฐ๋ ˆ๋“œ ๋™๊ธฐํ™”๋ฅผ ํ•˜์ง€์•Š์œผ๋ฉด, ์‹ฌ

jiwondev.tistory.com

 

 

๐Ÿ“Œ์Šค๋ ˆ๋“œ์— Lock์„ ๊ฑฐ๋Š” ์ด์œ 

๋™๊ธฐํ™”๋ฅผ ์œ„ํ•ด Lock์„ ๊ฑฐ๋Š” ์ด์œ ๋ฅผ ์กฐ๊ธˆ ๋” ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌํ•ด๋ณด์ž.

  • Critical Section(์ž„๊ณ„์˜์—ญ)์—์„œ Mutual Exclusion(์ƒํ˜ธ ๋ฐฐ์ œ)๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.
     โžก ์‰ฌ์šด๋ง๋กœ [๊ณต์œ ํ•˜๋Š” ๋ฐ์ดํ„ฐ์˜์—ญ]์—์„œ [ํ•œ ์Šค๋ ˆ๋“œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉ ๋ฐ ์ œํ•œ]ํ•˜๊ธฐ ์œ„ํ•จ.
  • ์Šค๋ ˆ๋“œ๊ฐ„์˜ ํ˜‘์—…๊ณผ ๋™๊ธฐํ™”์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.
     โžก ํŠน์ • ์ƒํƒœ์— ๋„๋‹ฌํ•  ๊ฒฝ์šฐ, ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์•Œ๋ ค์ฃผ์–ด ๋‹ค์Œ ์ž‘์—…์„ ๋ฌธ์ œ์—†์ด ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ž‘์—…์„ ๋™๊ธฐํ™”ํ•ด์ค€๋‹ค.
  • ๋ฐ์ดํ„ฐ์˜ ๊ฐ€์‹œ์„ฑ (Visiblity)๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค.
     โžก volatile ํ‚ค์›Œ๋“œ์ฒ˜๋Ÿผ ์บ์‹œ์—๋งŒ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์–ด ๋ณ€๊ฒฝ๋˜์—ˆ์Œ์—๋„ ๋ฐ์ดํ„ฐ ๊ฐ’์ด ๋™๊ธฐํ™” ๋˜์ง€ ์•Š๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

๋”ฑํžˆ ์ˆœ์„œ๊ฐ€ ํ•„์š”ํ•œ ์ž‘์—…์ด ์žˆ๋Š” ๊ฒƒ๋„ ์•„๋‹Œ๋ฐ [๊ณต์œ  ๋ฐ์ดํ„ฐ์˜ ๊ฐ’]์„ ์ฝ๊ฑฐ๋‚˜ ์—…๋ฐ์ดํŠธํ•  ๋•Œ๋งˆ๋‹ค ์ด๋ ‡๊ฒŒ ์Šค๋ ˆ๋“œ Lock์„ ๊ฑธ๊ฒŒ๋œ๋‹ค๋ฉด ์•ฑ์˜ ์„ฑ๋Šฅ์€ ํฌ๊ฒŒ ์ €ํ•˜๋œ๋‹ค.

 

๊ทธ๋ž˜์„œ Lock์„ ๊ฑธ๊ธฐ๋ณด๋‹ค๋Š”, ์•„์˜ˆ ์—ฐ์‚ฐ์˜ ์›์ž์„ฑ (Atomic)์„ ๋ณด์žฅํ•˜๋Š” ๋ณ€์ˆ˜, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ํ•œ๋‹ค.

 

 

 

๐Ÿ“Œ์›์ž์„ฑ์„ ๋ณด์žฅํ•œ๋‹ค์˜ ์˜๋ฏธ

์•„๋ž˜์˜ ํ•œ ์ค„ ์ฝ”๋“œ๋ฅผ ๋ณด์ž. ํ•ด๋‹น ์ฝ”๋“œ๋Š” ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๊ณ  ์žˆ์„๊นŒ?

// ์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ์ž‘์—…์ผ๊นŒ?
value = value + 1;

 

์ฝ”๋“œ๋ฅผ ๋‹จ ํ•œ์ค„๋ฐ–์— ์ž‘์„ฑํ•˜์ง€ ์•Š์•˜๊ณ , ๋‹จ์ˆœํžˆ 1์„ ๋”ํ•˜๊ณ  ์žˆ๊ธฐ๋•Œ๋ฌธ์— ๋ณ„ ๋ฌธ์ œ๊ฐ€ ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•˜์ง€๋งŒ ํ•ด๋‹น ์ฝ”๋“œ๋Š” CPU์— ์˜ํ•ด ์ฒ˜๋ฆฌ๋  ๋•Œ ์•„๋ž˜์™€ ๊ฐ™์€ ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๊ฑฐ์น˜๊ฒŒ ๋œ๋‹ค.

LOAD - value์˜ ๊ฐ’์„ ๋ ˆ์ง€์Šคํ„ฐ๋กœ ๋กœ๋”ฉํ•จ.
ADD - ๋ ˆ์ง€์Šคํ„ฐ์˜ ๊ฐ’์„ 1์„ ๋”ํ•จ
STORE - ๋ ˆ์ง€์Šคํ„ฐ์˜ ๊ฐ’์„ value ๋ฉ”๋ชจ๋ฆฌ๋กœ ์ €์žฅํ•จ.

 

์ด 3๊ฐœ์˜ ์—ฐ์‚ฐ์ด [์ „๋ถ€ ์‹คํ–‰๋˜์ง€ ์•Š๊ฑฐ๋‚˜] or [ํ•ญ์ƒ ํ•จ๊ป˜ ์‹คํ–‰๋จ]์„, ์ฆ‰ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๋งํ•  ์ˆ˜ ์žˆ์„๊นŒ?

1. A ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ’์„ ๋ ˆ์ง€์Šคํ„ฐ์— LOADํ•˜๊ณ , ๋”ํ•˜๊ณ  ์žˆ๋Š” ์™€์ค‘์—
2. B ์Šค๋ ˆ๋“œ๊ฐ€ ์ ‘๊ทผํ•ด์„œ ๊ทธ ๊ฐ’์„ ์ฝ๊ณ  ์ˆ˜์ •ํ•˜๊ณ  STORE๋กœ ์ €์žฅํ•˜์˜€๋‹ค.
3. ๊ทธ๋ฆฌ๊ณ  A ์Šค๋ ˆ๋“œ๊ฐ€ ๊ฐ’์„ ๋‹ค์‹œ STOREํ•˜๋ฉด ์–ด๋–ค ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ• ๊นŒ?

์ฆ‰, value = value + 1; ์—ฐ์‚ฐ์€ ์›์ž์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ์—ฐ์‚ฐ์ด ์•„๋‹ˆ๋‹ค. ์ฆ‰, ๊ฐ๊ฐ์˜ ์—ฐ์‚ฐ ์ค‘๊ฐ„์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ผ์–ด๋“ค์–ด ๋ฉ”๋ชจ๋ฆฌ ๊ฐ’์„ ๋ฎ์–ด ์“ฐ๊ฑฐ๋‚˜ ์ˆ˜์ •์ค‘์ธ ๋”๋Ÿฌ์šด ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์ด๋‹ค.

 

 

 

๐Ÿ“Œ Atomic ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ์›์ž์„ฑ์„ ๋ณด์žฅํ• ๊นŒ?

๊ทธ๋ ‡๋‹ค๋ฉด java.util.concurrent.atomic ์€ ์–ด๋–ป๊ฒŒ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ง์…ˆ์„ ํ•˜๋Š”๊ฑธ๊นŒ?

import java.util.concurrent.atomic.AtomicInteger;
 
private AtomicInteger counter = new AtomicInteger();
 
public int getNextUniqueIndex() {
    return counter.getAndIncrement();
}

 

AtomicInteger๋ฅผ ๋œฏ์–ด๋ณด์ž. getAndIncrement()๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋™์ž‘ํ•œ๋‹ค.

public long incrementAndGet() {

    long oldValue;
    long newValue;

    do {
        oldValue = value;
        newValue = oldValue + 1;
    } while (!compareAndSwap(value, oldValue, newValue));

    return newValue;

}

ํ”ํžˆ ์ด๋Ÿฐ ๋ฐฉ์‹์„ CAS (Compare And Swap)์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค. Atomic ํด๋ž˜์Šค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ CAS๋กœ ๋™์ž‘ํ•œ๋‹ค.

๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ธฐ ์ „, ์Šค๋ ˆ๋“œ์˜ ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ(oldValue)์— ์ €์žฅํ•˜๊ณ  ๊ฐ’์„ ๊ณ„์‚ฐํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์—ฐ์‚ฐ์„ ์™„๋ฃŒํ•œ ํ›„ ํ˜„์žฌ ์กด์žฌํ•˜๋Š” ๊ฐ’๊ณผ ์ €์žฅํ•ด๋†จ๋˜ oldValue๊ฐ€ ๊ฐ™์„ ๋•Œ๋งŒ ์ž‘์—…์„ ์ง„ํ–‰ํ•˜๊ณ , ๋งŒ์•ฝ ๋‹ค๋ฅด๋‹ค๋ฉด ์—ฐ์‚ฐ ์ž์ฒด๋ฅผ ์ทจ์†Œํ•˜๊ณ  ๋‹ค์‹œ ์ง„ํ–‰ํ•˜์—ฌ ์—ฐ์‚ฐ์˜ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

 

๋ฌผ๋ก  CAS ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด Race Conditions, ์Šค๋ ˆ๋“œ ๊ฒฝํ•ฉ์ด ์‹ฌํ•˜๋‹ค๋ฉด CPU ์‚ฌ์šฉ๋ฅ ์ด ์ฆ๊ฐ€ํ•˜๊ฒ ์ง€๋งŒ, ์ ์–ด๋„ Lock์œผ๋กœ ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์•„๋ฌด๊ฒƒ๋„ ๋ชปํ•˜๊ฒŒ ์ž ๊ถˆ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ ๋ณด๋‹ค๋Š” ์„ฑ๋Šฅ์ ์ธ ์ธก๋ฉด์—์„œ๋Š” ํ›จ์”ฌ ์ข‹๋‹ค.

 

๋˜ํ•œ Atomic์€ JVM์—์„œ ์ œ๊ณตํ•˜๋Š” Unsafe ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ์ด๋Š” native ๋ฉ”์„œ๋“œ๋กœ ์ด๋ฃจ์–ด์ ธ์žˆ๋Š”๋ฐ ์ž๋ฐ”์—์„œ C/CPP ์ฒ˜๋Ÿผ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๊ฒŒ ๋งŒ๋“  ์ €์ˆ˜์ค€ ํฌ์ธํ„ฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ง์ ‘ ๋‹ค๋ฃจ๋Š” ๋งŒํผ ์„ฑ๋Šฅํ–ฅ์ƒ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ทธ๋งŒํผ ์œ„ํ—˜์„ฑ์„ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๊ฒŒ ์ƒ์„ฑ์ž์™€ ํŒฉํ† ๋ฆฌ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ง‰ํ˜€์žˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณธ๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

public class AtomicInteger extends Number implements java.io.Serializable {
    /*
     * This class intended to be implemented using VarHandles, but there
     * are unresolved cyclic startup dependencies.
     */
    private static final Unsafe U = Unsafe.getUnsafe();
    
    public final int incrementAndGet() {
        return U.getAndAddInt(this, VALUE, 1) + 1;
    }
}

 

 

 

๐Ÿ“Œ Atomic Adder์™€ Accumulator, ๊ฒฝํ•ฉ์˜ ์ตœ์†Œํ™”

์œ„์—์„œ ์„ค๋ช…ํ•œ๋Œ€๋กœ ๋‹จ์ˆœํžˆ CAS๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๋ฉด, ์Šค๋ ˆ๋“œ์— Lock์„ ๊ฑธ์ง€ ์•Š๊ณ  ๋ฉ”๋ชจ๋ฆฌ์˜ ์›์ž์„ฑ์€ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ฐ’์„ ์ˆ˜์ •ํ•˜์ง€์•Š๊ณ  '์ฝ๊ธฐ'๋งŒ ๋งŽ์ด ํ•œ๋‹ค๋ฉด ํŠน๋ณ„ํ•œ ๋™์ž‘์—†์ด ๊ทธ๋Œ€๋กœ ํ›จ์”ฌ ์„ฑ๋Šฅ์ด ์ข‹์„ ๊ฒƒ์ด๋‹ค.

 

ํ•˜์ง€๋งŒ ์ˆ˜์ •์ด ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด? ์ฆ‰ ์Šค๋ ˆ๋“œ๋“ค์ด ์„œ๋กœ ์—Ž์น˜๋ฝ ๋’ค์น˜๋ฝํ•˜๋ฉฐ ์‹คํ–‰ / ๋กค๋ฐฑ์„ ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋˜๋ฉฐ CPU์˜ ์‚ฌ์šฉ๋ฅ ์ด ์ฆ๊ฐ€ํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ด๋ผ๊ณ  Java Atomic์—์„œ๋Š” Adder์™€ Accumulator ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋”ฐ๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค.

 

AtomicLong์œผ๋กœ ์˜ˆ๋ฅผ ๋“ค๋ฉด LongAdder์™€ LongAccumulator๊ฐ€ ์กด์žฌํ•œ๋‹ค. ์ด ๋‘ ํด๋ž˜์Šค๋Š” API๋งŒ ์กฐ๊ธˆ ๋‹ค๋ฅผ ๋ฟ, ๋‚ด๋ถ€ ๊ตฌ์กฐ๋Š” ๋น„์Šทํ•˜๋‹ค. CAS ์—ฐ์‚ฐ์—์„œ ๊ฒฝํ•ฉ ๊ณผ์ •์— ์˜ํ•œ CPU ์†Œ๋ชจ๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•ด ๊ณ ์•ˆ๋œ ๋ฉ”์„œ๋“œ๋“ค์ด๋‹ค.

๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜์ž๋ฉด, [๊ณต์šฉ ๋ฉ”๋ชจ๋ฆฌ์ธ Base]์™€ [์Šค๋ ˆ๋“œ ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ์ธ Cell]๋กœ ๋‚˜๋ˆˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Base ๋ณ€์ˆ˜๋ฅผ CAS ๋ฐฉ์‹์œผ๋กœ ์ฝ๋‹ค๊ฐ€ ๋งŒ์•ฝ ์‹คํŒจํ•œ๋‹ค๋ฉด while(...)๋กœ ๋ฐ˜๋ณตํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ ์Šค๋ ˆ๋“œ ๋ณ„๋กœ Cell ๋ฉ”๋ชจ๋ฆฌ์— ๋ณ„๋„์˜ ์—ฐ์‚ฐ์„ ์ง„ํ–‰ํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ ์ตœ์ข…์ ์œผ๋กœ ๊ฐ ์Šค๋ ˆ๋“œ์˜ ๊ฐ’์„ ํ•ฉ์ณ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

์„ธ๋กœ๋Š” ์—ฐ์‚ฐ์— ์†Œ์š”๋˜๋Š” ์‹œ๊ฐ„์„, ๊ฐ€๋กœ๋Š” ๋™์ž‘ํ•˜๋Š” ์Šค๋ ˆ๋“œ์˜ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถ„์‚ฐํ•ด์„œ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์„ ์ŠคํŠธ๋ผ์ดํ•‘(Striping)์ด๋ผ ํ•˜๋ฉฐ ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋™์ ์œผ๋กœ ๋™์ž‘์ด ๋‹ฌ๋ผ์ง„๋‹ค๊ณ  ํ•˜์—ฌ ์ด ๋ฐฉ์‹์„ Dynamic Striping ์ด๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

 

 

๐Ÿ“Œ ์šด์˜์ฒด์ œ์˜ ์ดํ•ด, CPU Cache์˜ False Sharing ์ตœ์†Œํ™”

์œ„์˜ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๋ณด๋ฉด ์•Œ๊ฒ ์ง€๋งŒ, Cell ์˜์—ญ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์ƒ์— ๊ทผ์ ‘ํ•  ์ˆ˜ ๋ฐ–์— ์—†๊ฒŒ๋œ๋‹ค. 

 

์šด์˜์ฒด์ œ๋Š” CPU cache๋Š” ์ง€์—ญ ์ฐธ์กฐ์„ฑ์„ ๋ฐ”ํƒ•์œผ๋กœ ์„ค๊ณ„๋˜์–ด์žˆ๋‹ค. ์ปดํ“จํ„ฐ ํ”„๋กœ๊ทธ๋žจ์˜ ํŠน์„ฑ์ƒ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฐ๋‹ฌ์•„ ์ฝ์„ ํ™•๋ฅ ์ด ๋†’์œผ๋ฏ€๋กœ ๋‹ค์Œ ๋ฐ์ดํ„ฐ๋ฅผ CPU์— Cacheํ•ด๋†”์„œ ์„ฑ๋Šฅ์„ ๋†’์ธ๋‹ค.

 

์ฆ‰ Cell ์˜์—ญ์€ Cpu Cache์˜ ํ•œ ๋ผ์ธ์— ์œ„์น˜ํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ณ , ํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ์ž์‹ ์˜ Cell์„ ๋ณ€๊ฒฝํ•  ๊ฒฝ์šฐ ๊ฐ™์€ ๋ผ์ธ์— ์žˆ๋Š” ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์˜ Cpu Cache ์˜์—ญ๋„ ๊ณ„์† ์—…๋ฐ์ดํŠธํ•˜๊ฒŒ๋œ๋‹ค. ์ฆ‰ ์“ธ๋•Œ ์—†์ด ์บ์‹œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ณต์œ ํ•ด ์„ฑ๋Šฅ์ด ๋‚˜๋น ์ง€๋Š” False Sharing (๊ฑฐ์ง“ ๊ณต์œ )๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.

 

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Atomic ์—์„œ๋Š” ์ด๋Š” Cell์˜ ํฌ๊ธฐ๊ฐ€ Cpu Cache Line์˜ ํฌ๊ธฐ์™€ ๊ฐ™์•„์ง€๋„๋ก ๋นˆ ๊ณต๊ฐ„์„ ๋ถ™์—ฌ Paddingํ•˜๋„๋ก ๋งŒ๋“ ๋‹ค.

offset๊ณผ delta๊ฐ€ ๋ฌด์Šจ ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•Œ๊ฒ ๋Š”๊ฐ€?

 

 

๐Ÿ“Œ ๊ทธ๋ž˜์„œ Atomic์ด Synchroized๋ณด๋‹ค ์™œ ์ข‹๋‹ค๊ตฌ์š”?

๋‹ค์‹œ ๋ณธ๋ก ์œผ๋กœ ๋Œ์•„๊ฐ€๋ณด์ž. Atomic์€ ์–ด๋–ป๊ฒŒ ์›์ž์„ฑ์„ ๋ณด์žฅํ•˜๋ฉฐ, ์™œ ์„ฑ๋Šฅ์ด ์ข‹์€ ๊ฒƒ์ผ๊นŒ?

  • CAS ๋ฐฉ์‹์œผ๋กœ ์Šค๋ ˆ๋“œ Lock ์—†์ด ๋™๊ธฐํ™”๋ฅผ ๋ณด์žฅํ•œ๋‹ค.
  • Unsafe๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ €์ˆ˜์ค€์œผ๋กœ ๋‹ค๋ค„ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•œ๋‹ค.
  • ์“ฐ๊ธฐ๊ฐ€ ๋งŽ์ด ๋ฐœ์ƒํ•ด์„œ ์Šค๋ ˆ๋“œ ๊ฒฝํ•ฉ(Racing)์ด ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, ํ•„์š”์—†๋Š” ๊ฐ€์‹œ์„ฑ ๋ณด์žฅ(volatile)์ด ์ž์ฃผ ๋ฐœ์ƒํ•œ๋‹ค.
  • ์ด ๊ฒฝ์šฐ Atomic์˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งŽ์€ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ์ฝ๊ธฐ์—๋Š” ๊ทธ๋ƒฅ ์‚ฌ์šฉํ•˜๋‹ค๊ฐ€ Dynamic Striping ์„ ํ†ตํ•ด ๊ฒฝํ•ฉ์ด ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์Šค๋ ˆ๋“œ ๋ณ„ ๋ฉ”๋ชจ๋ฆฌ(Cell)์— ๋”ฐ๋กœ ์—ฐ์‚ฐํ•œ ํ›„, ๋‚˜์ค‘์— ํ•ฉ์น˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  • ๋˜ํ•œ Cpu Cache์˜ False Sharing์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด Cpu Cache Line์˜ ํฌ๊ธฐ๋กœ ํŒจ๋”ฉ์„ ๊ตฌํ˜„ํ•œ๋‹ค.

 


๐Ÿ“Œ ConcurrentHashmap์˜ Lock Striping

ConcurrentHashMap์€ ์–ด๋–ป๊ฒŒ ์›์ž์„ฑ์„ ๋ณด์žฅํ• ๊นŒ? ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณด์ž.

 

๋‹จ์ˆœํžˆ ๋ฉ”์„œ๋“œ์— synchroized๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋™๊ธฐํ™”๋ฅผ ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์„ ๊ฒƒ์ด๋‹ค.

class SharedData {
    private int intData;
    private boolean boolData;

    public synchronized int getInt() { return intData; }
    public synchronized void setInt(int n) { intData = n; }
    public synchronized boolean getBool() { return boolData; }
    public synchronized void setBool(boolean b) { boolData = b; }
};

 

์ด๋ ‡๊ฒŒ ํ•œ๋‹ค๋ฉด ๋”ฑํžˆ Lock์„ ๊ฑธ ํ•„์š”๊ฐ€ ์—†์Œ์—๋„ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฉˆ์ถฐ๋ฒ„๋ ค ์„ฑ๋Šฅํžˆ ์ƒ๋‹นํžˆ ๋‚˜๋น ์ง„๋‹ค.

๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋ ˆ์ฝ”๋“œ๋ณ„๋กœ Lock์„ ๊ฑฐ๋Š” ๊ฒƒ ์ฒ˜๋Ÿผ ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด๋“ฑ์„ ์ด์šฉํ•ด์„œ Lock์„ ์—ฌ๋Ÿฌ๊ฐœ๋กœ ์ชผ๊ฐ ๋‹ค.

class SharedData {
    private int intData;
    private boolean boolData;
    private Object intSync = new Object(); // ์ฐธ๊ณ ๋กœ intData ์ž์ฒด๋ฅผ Lock์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ์ด ๊ตฌ๋ฆฌ๋‹ค.
    private Object boolSync = new Object(); // ์ฝ๊ธฐ ๊ฐ™์€ ํ•„์š”์—†๋Š” ๋™์ž‘์—๋„ Lock์ด ๊ฑธ๋ฆฌ๋‹ˆ๊นŒ.
	
    // intSync, boolSync ๋กœ ๊ฐ๊ฐ ๋‹ค๋ฅธ Lock์„ ๊ฐ€์ง€๋ฉฐ, ์ด ๋‘ ๊ฐœ๋Š” ๋™์‹œ์— ์‹คํ–‰ ๊ฐ€๋Šฅํ•˜๋‹ค.
    public int getInt() { synchronized (intSync) { return intData; } }
    public void setInt(int n) { synchronized (intSync) { intData = n; } }
    public boolean getBool() { synchornized (boolSync) { return boolData; } }
    public void setBool(boolean b) { synchronized (boolSync) { boolData = b; } }
}

์ด๋ฅผ Lock์„ ์—ฌ๋Ÿฌ๊ฐœ๋กœ ๋‚˜๋ˆด๋‹ค๋Š” Lock Splitting ๋ฐฉ์‹์ด๋ผ ๋ถ€๋ฅธ๋‹ค. (* ์•„์ง ๋ถ„์‚ฐ ์ €์žฅ์„ ์˜๋ฏธํ•˜๋Š” Striping ์ด ์•„๋‹ˆ๋‹ค.)

 

 

๐Ÿ“Œ SharedData๋ฅผ ๋” ๊ฐœ์„ ํ•  ๋ฐฉ๋ฒ•์ด ๋– ์˜ค๋ฅด์ง€ ์•Š๋Š”๊ฐ€?

์šฐ๋ฆฌ๊ฐ€ ์œ„์—์„œ ํ–ˆ๋˜ ๋ฐฉ๋ฒ•. Atomic ์ž๋ฃŒํ˜•์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿผ ๋ณต์žกํ•œ ์ฒ˜๋ฆฌ ์—†์ด Lock Striping(์ŠคํŠธ๋ผ์ดํ•‘)์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๊ณ , ์ด๋Š” Lock์„ ๊ฑธ์ง€ ์•Š์•„ ์œ„์˜ ๋ฐฉ์‹๋ณด๋‹ค ์„ฑ๋Šฅ์ด ํ›จ์”ฌ ์ข‹๋‹ค.

class SharedData {
    private AtomicInteger intData = new AtomicInteger(0);
    private AtomicBoolean boolData = new AtomicBoolean(false);

    public int getInt() { return intData.get(); }
    public void setInt(int n) { intData.set(n); }
    public boolean getBool() { return boolData.get(); }
    public void setBool(boolean b) { boolData.set(b); }
}

 

์‹ค์ œ ConcurrentHashmap์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด AtomicReference๋ฅผ ์‚ฌ์šฉํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

AtomicClass๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๋™์ž‘์€ ๋™์ผํ•˜์ง€๋งŒ, AtomicReference๋กœ ํ•œ๋ฒˆ ๋” ๊ฐ์‹ผ ์ด์œ ๋Š” HashMap ๊ตฌ์กฐ์ƒ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜์™€ ๊ด€๋ จ์žˆ๋‹ค. ๊ถ๊ธˆํ•˜๋ฉด WeakHashMap์˜ WeakReference ๋Œ€ํ•ด ์ฐพ์•„๋ณด๋ฉด ์ดํ•ดํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

public void atomicReference1() { // AtomicClass์™€ ์‚ฌ์šฉ๋ฒ•์— ํฐ ์ฐจ์ด๋Š” ์—†๋‹ค.
    AtomicReference<Integer> atomic = new AtomicReference<>();
    System.out.println("atomic : " + atomic.get()); // atomic.get() == null

    AtomicReference<Integer> atomic2 = new AtomicReference<>(10);
    System.out.println("atomic2 : " + atomic2.get()); // atomic.get() == 10
}

 

 

 

๐Ÿ“Œ ConcurrentHashMap ์˜ ํšจ์œจ์ ์ธ ๋™๊ธฐํ™”๋ฅผ ์œ„ํ•œ API

๊ทธ๋ƒฅ Map์„ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. ํ•ด๋‹น ์ฝ”๋“œ๋Š” ์›์ž์„ฑ์„ ๋ณด์žฅ ํ•˜๋Š”๊ฐ€?

if (map.containsKey(key) == false) {
    map.put(key, value);
} else {
    doSomthing();
}

์•„๋‹ˆ๋‹ค. ๋น„๊ต๋ฅผ ํ•˜๋Š” ๋™์ž‘๊ณผ put์œผ๋กœ ์‚ฝ์ž…ํ•˜๋Š” ๋™์ž‘์‚ฌ์ด์— ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๊ฐ€ ์นจ๋ฒ”ํ•˜์—ฌ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ConcurrentHashMap์€ ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ด์ค˜์„œ ํ•ด๋‹น ๋™์ž‘์˜ ์›์ž์„ฑ์„ ๋ณด์žฅํ•ด์ค€๋‹ค.

// ์ฐธ๊ณ ๋กœ putIfAbsent๋Š” ํ•ด๋‹น ๊ฐ’์ด ์—†๋‹ค๋ฉด ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
// ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ๋Š” ํ˜„์žฌ Map์— ์—…๋ฐ์ดํŠธ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. (์ฆ‰ ์ด๋ฏธ ์žˆ๋‹ค๋ฉด ๊ธฐ์กด ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•จ.)
if (map.putIfAbsent(key, value) != value) {
    doSomthing();
}

 

๋ธ”๋กœ๊ทธ์˜ ์ •๋ณด

JiwonDev

JiwonDev

ํ™œ๋™ํ•˜๊ธฐ