AtomicReference활용해서 락 프리 큐 구현을 설명. 강의 참고!

락 프리가 무조건 좋은게아니다. 하드웨어 환경, 경합하는 스레드가 큰 경우, 다루는 데이터의 크기 등을 모두 고려하고 테스트해봐야된다.

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
 
class SpinLockBool {
    private final AtomicBoolean owner = new AtomicBoolean(false);
 
    public void lock() {
        while (!owner.compareAndSet(false, true))
            LockSupport.parkNanos(1);
    }
 
    public void unlock() {
        owner.set(false);
    }
}
 
class UserData {
    UserData(String name) {
        this.name = name;
    }
    String name;
    UserData next;
}
 
class MyList {
    //protected SpinLockBool lock = new SpinLockBool();
    protected ReentrantLock lock = new ReentrantLock();
    protected UserData headNode = new UserData("DummyHead");
    protected UserData tailPointer = headNode;
 
    public int getCount() {
        int count = 0;
        UserData tmp = headNode.next;
        while(tmp != null) {
            ++count;
            tmp = tmp.next;
        }
        return count;
    }
 
    public void appendNode(String name) {
        UserData newUser = new UserData(name);
        lock.lock();
          tailPointer.next = newUser;
          tailPointer = newUser;
        lock.unlock();
    }
}
 
class LockFreeUser {
    LockFreeUser(String name) {
        this.name = name;
        next = new AtomicReference<LockFreeUser>(null);
    }
    String name;
    AtomicReference<LockFreeUser> next;
}
 
class LockFreeList {
    protected LockFreeUser headNode;
    protected AtomicReference<LockFreeUser> tail;
 
    LockFreeList() {
        headNode = new LockFreeUser("DummyHead");
        tail = new AtomicReference<LockFreeUser>(headNode);
    }
 
    public int getCount() {
        int count = 0;
        LockFreeUser tmp = headNode.next.get();
        while(tmp != null) {
            ++count;
            tmp = tmp.next.get();
        }
        return count;
    }
 
    public void push(String name) {
        LockFreeUser newUser = new LockFreeUser(name);
        while (true) {
            LockFreeUser last = tail.get(); //last = tail;
            LockFreeUser next = last.next.get(); //next = last.next;
            if (last == tail.get()) { //last == tail;
                if (next == null) { //last node?
                    if (last.next.compareAndSet(null, newUser)) {
                        //last.next = newUser;
                        tail.compareAndSet(last, newUser); //tail = newUser;
                        return;
                    }
                }
                else
                    tail.compareAndSet(last, next);
            }
            LockSupport.parkNanos(1);
        }
    }
}
 
class TestThread extends Thread {
    TestThread(MyList db) {
        list = db;
    }
 
    private final MyList list;
    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++)
            list.appendNode("TEST" + i);
    }
}
 
class LockFreeTestThread extends Thread {
    LockFreeTestThread(LockFreeList db) {
        list = db;
    }
 
    private final LockFreeList list;
    @Override
    public void run() {
        for (int i = 0; i < 1000000; i++)
            list.push("TEST" + i);
    }
}
 
public class Main {
    private static void testNormalList() throws InterruptedException {
        MyList db = new MyList();
        int threadCount = 2;
        TestThread[] threads = new TestThread[threadCount];
 
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < threadCount; i++)
            threads[i] = new TestThread(db);
        for (int i = 0; i < threadCount; i++)
            threads[i].start();
        for (int i = 0; i < threadCount; i++)
            threads[i].join();
        long endTime = System.currentTimeMillis();
 
        System.out.println("Duration: " + (endTime - beginTime) + " ms");
        System.out.println("getCount(): " + db.getCount());
    }
 
    private static void testLockfreeList() throws InterruptedException {
        LockFreeList db = new LockFreeList();
        int threadCount = 2;
        LockFreeTestThread[] threads = new LockFreeTestThread[threadCount];
 
        long beginTime = System.currentTimeMillis();
        for (int i = 0; i < threadCount; i++)
            threads[i] = new LockFreeTestThread(db);
        for (int i = 0; i < threadCount; i++)
            threads[i].start();
        for (int i = 0; i < threadCount; i++)
            threads[i].join();
        long endTime = System.currentTimeMillis();
 
        System.out.println("Duration: " + (endTime - beginTime) + " ms");
        System.out.println("getCount(): " + db.getCount());
    }
 
    public static void main(String[] args) throws InterruptedException {
        testNormalList();
        testLockfreeList();
    }
}