Atomic Type

Atomic Type은 단일 변수에 대해서 Atomic Operations을 지원한다.

Wrapping 클래스의 일종으로, 참조 타입과 원시 타입 두 종류의 변수에 모두 적용이 가능하다. 사용시 내부적으로 Compare -And-Swap 알고리즘?을 사용해 lock 없이 동기화 처리를 할 수 있다.

간단 예제

class MyLock {
	private var locked = false

	fun tryLock(): Boolean {
		// 강제로 비용이 큰 작업 발생
		for (i in 1..100_000) {}

		if (!locked) {
			locked = true
			return true
		}
		return false
	}
}

class TestRunnable(private val myLock: MyLock): Runnable {
	override fun run() {
		println("${Thread.currentThread().name}-${mylock.tryLock()}")
	}
}

fun main() {
	val myLock = MyLock()
	
	for (i in 1..100_000) {
		Thread(TestRunnable(myLock)).start()
	}
}

스크린샷 2022-01-04 오전 9.51.41.png

위 코드를 실행해 보면 true와 false가 섞여 있는 것을 볼 수 있다. 여기서 원자성을 보장하고 싶다면 boolean 타입을 AtomicBoolean으로 바꾸기만 하면 된다.

class MyLock {
	private val locked = AtomicBoolean()

	fun tryLock(): Boolean {
		if (!locked.get()) {
			// 강제로 비용이 큰 작업 발생
			for (i in 1..100_000) {}
		}
		return locked.compareAndSet(false, true)
	}
}

스크린샷 2022-01-04 오전 9.55.47.png

처음 접근한 쓰레드를 제외하곤 모두 락을 획득하지 못해 false가 리턴되는걸 볼 수 있다.

Compare-And_Swap

CAS 알고리즘은 현재 주어진 값(현재 쓰레드에서의 데이터)과 실제 모리 데이터를 비교해서 두 개가 일치할 때만 값을 업데이트 한다고 한다. 이 역할을 하는 메소드가 compareAndSet() 이다. 즉 synchronized 처럼 임계영역에 같은 시점에 두개 이상의 쓰레드가 접근하려 하면 쓰레드 자체를 blocking 시키는 알고리즘이다.

Untitled