一種重置生產者消費者模式的消費隊列的線程安全的做法(java)

/*
 * @author lihong
 * @date 2016年1月1日 下午5:20:02
 * @version v1.0
 */
package com.lihong.DDPush;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * 生產者只有一個,消費者有多個,每次消費者都會把整個老隊列拿走,新建一個隊列替換老的隊列,
 * 以便生產者生產的數據可以繼續存儲下來。  但要保證,生產者生產的數據不能被重複消費。 一個
 * 消費線程消費了,就不能被另一個消費線程消費了
 *
 * @author lihong 2016年1月1日 下午5:20:02
 * @version v1.0
 */
public class Cache {
    private static final Cache INSTANCE = new Cache();
    private volatile List<User> queue = new LinkedList<User>();

//    沒必要用AtomicReferenceFieldUpdater,  可以使用AtomicReference就好了
    private static final AtomicReferenceFieldUpdater queueUpdater =
            AtomicReferenceFieldUpdater.newUpdater(Cache.class, List.class, "queue");

    private List<User> getQueue() {
        return queue;
    }

    //唯一的生產者調用該方法
    public static void produce(User user) {
        INSTANCE.getQueue().add(user);
    }

    //多個消費者的調用該方法
    public static List<User> consume() {

//  volatile保證可見性後,但如果某幾個消費線程是在同一瞬間調用重置隊列方法,
//  結果很可能拿到了同一個老隊列的引用,造成重複消費。使用CAS(compareAndSet),目的是不用鎖,又能避免重複消費。
        List<User> oldQueue = INSTANCE.getQueue();
        List<User> newQueue = new LinkedList<User>();
        while (!queueUpdater.compareAndSet(INSTANCE, oldQueue, newQueue)) {
            oldQueue = INSTANCE.getQueue();
        }
        return oldQueue;
    }

    public class User {
    }
}

發佈了120 篇原創文章 · 獲贊 132 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章