자바에서는 멀티스레드 환경에서 하나의 변수를 여러 스레드가 동시에 읽고 쓰게되면 race condition이 발생한다. 이를 해결하려면 synchronized 같은 락을 걸어야 하지만, 락은 성능에 영향을 줄 수 있기에 Atomic 클래스를 사용한다.
AtomicIntegerAtomicLongAtomicBooleanAtomicReference이들은 CAS(Compare-And-Set) 방식으로 락 없이 원자적 연산을 보장한다.
long 값을 하나 보관volatile과 메모리 가시성을 보장 -> 다른 스레드에서 즉시 반영된 값 확인 가능AtomicLong counter = new AtomicLong(0);
// 값 읽기
long value = counter.get();
// 값 설정
counter.set(10);
// 원자적 증가
counter.incrementAndGet(); // 1 증가 후 결과 반환
counter.getAndIncrement(); // 현재 값 반환 후 1 증가
AtomicLong() : 초기값이 0인 AtomicLong을 생성AtomicLong(longVal) : 인자의 값으로 초기화된 AtomicLong 생성| 메서드 | 설명 |
|---|---|
get() | 현재 값 읽기 |
set(long newValue) | 새로운 값으로 설정 |
getAndIncrement() | 현재 값 반환 후 +1 (후증가) |
incrementAndGet() | +1 후 증가된 값 반환 (전증가) |
getAndAdd(long delta) / addAndGet(long delta) | delta만큼 더하기 |
compareAndSet(expect, update) | 현재 값이 expect일 때만 update로 교체 |
compareAndSet을 이용하면 특정 로직을 최초 한 번만 실행하도록 할 수 있다.
import java.util.concurrent.atomic.AtomicLong;
class Once {
private AtomicLong done = new AtomicLong(0);
public void runOnce(Runnable action) {
if (done.compareAndSet(0, 1)) {
action.run();
}
}
}
public class Test {
public static void main(String[] args) {
Once once = new Once();
once.runOnce(() -> System.out.println("Hello World!"));
once.runOnce(() -> System.out.println("두 번째 실행은 무시됨"));
}
}
Task 클래스: name, priority(정수, 클수록 우선순위 높음).Scheduler 클래스addTask(Task task)getNextTask() → 가장 높은 우선순위 반환 (동점이면 먼저 들어온 순서)public class test2 {
public static void main(String[] args) {
Scheduler scheduler = new Scheduler();
scheduler.addTask(new Task("A", 1));
scheduler.addTask(new Task("B", 3));
scheduler.addTask(new Task("C",1));
System.out.println(scheduler.getNextTask());
System.out.println(scheduler.getNextTask());
}
}
class Task{
String name;
int priority;
long seq;
public Task(String name, int priority){
this.name = name;
this.priority = priority;
}
@Override
public String toString(){
return name;
}
}
class Scheduler{
private PriorityQueue<Task> queue;
private AtomicLong sequence = new AtomicLong(0);
public Scheduler(){
queue = new PriorityQueue<>((t1, t2) -> {
if(t1.priority != t2.priority){ // 우선순위가 같지 않다면
return Integer.compare(t2.priority, t1.priority);
} else { // 우선순위가 같다면 들어온 순서대로
return Long.compare(t1.seq, t2.seq);
}
});
}
public void addTask(Task task){
task.seq = sequence.getAndIncrement();
queue.offer(task);
}
public String getNextTask(){
Task task = queue.poll();
return task == null ? null : task.name;
}
}