package com.elab.example.springbootdemo.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 手写阻塞队列
 *
 * @author ： liukx
 * @time ： 2019/8/14 - 17:10
 */
public class XBlockingQueue<T> {
    private Logger logger = LoggerFactory.getLogger(XBlockingQueue.class);
    public List<T> queue = new ArrayList<>();
    public volatile int size;
    public volatile int index = 0;

    private Lock lock = new ReentrantLock();
    private Condition isFull = lock.newCondition();
    private Condition isNull = lock.newCondition();

    public XBlockingQueue(int size) {
        this.size = size;
    }


    public void add(T obj) throws Exception {
        lock.lock();
        try {
            if (index == size) {
                logger.info(" 阻塞队列满啦 ... ");
                isFull.await();
                index = -1;
            }
            queue.add(index, obj);
            isNull.signal();
            index++;
        } finally {
            lock.unlock();
        }
    }

    public T take() throws Exception {
        lock.lock();
        try {
            if (index == 0) {
                logger.info(" 阻塞队列中没有值...");
                isNull.await();
            }
//            T t = queue.get(index);
            T t = queue.get(index);
            logger.info("take : " + t.toString());
            isFull.signal();
            --index;
            return t;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws Exception {
        XBlockingQueue queue = new XBlockingQueue(10);

        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    queue.add(i);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(1000);
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                try {
                    queue.take();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();


//        for (int i = 0; i < 100; i++) {
//            int finalI = i;
//            new Thread(() -> {
//                try {
//                    queue.add(finalI);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }).start();
//        }
//        for (int i = 0; i < 100; i++) {
//            new Thread(() -> {
//                try {
//                    Object take = queue.take();
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }).start();
//        }

    }

}
