Java中的并发队列

1.队列

  • 队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部)就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.

  • 在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。队列会自动平衡负载。如果第一个线程集运行得比第二个慢,则第二个 线程集在等待结果时就会阻塞。如果第一个线程集运行得快,那么它将等待第二个线程集赶上来.说白了,就是先进先出,线程安全!

  • java中并发队列都是在java.util.concurrent并发包下的,Queue接口与List、Set同一级别,都是继承了Collection接口

2.实现一个简单的并发队列

import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author QiuQiu&LL
 * @version 1.0
 */
public class MyQueue {
    //元素集合
    private LinkedList<Object> list = new LinkedList<Object>();
    //计数器(同步),判断集合元素数量
    private AtomicInteger count = new AtomicInteger();
    //集合上限与下限,final必须指定初值
    private final int minSize = 0;
    private final int maxSize;

    //构造器指定最大值
    public MyQueue(int maxSize) {
        this.maxSize = maxSize;
    }

    //初始化对象,用于加锁,也可直接用this
    private Object lock = new Object();

    /**
     * put方法:往集合中添加元素,如果集合元素已满,则此线程阻塞,直到有空间再继续
     *
     * @param obj
     */
    public void put(Object obj) {
        synchronized (lock) {
            while (count.get() == this.maxSize) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            list.add(obj);
            //计数器加一
            count.incrementAndGet();
            System.out.println("放入元素:" + obj);
            //唤醒另一个线程,(处理极端情况:集合一开始就是空,此时take线程会一直等待)
            lock.notify();
        }
    }

    /**
     * take方法:从元素中取数据,如果集合为空,则线程阻塞,直到集合不为空再继续
     *
     * @return
     */
    public Object take() {
        Object result = null;
        synchronized (lock) {
            while (count.get() == this.minSize) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //移除第一个
            result = list.removeFirst();
            //计数器减一
            count.decrementAndGet();
            System.out.println("拿走元素:" + result);
            //唤醒另一个线程,(处理极端情况:集合一开始就是满的,此时put线程会一直等待)
            lock.notify();
        }
        return result;
    }

    public int getSize() {
        return this.count.get();
    }

    public static void main(String[] args) {
        //创建集合容器
        MyQueue queue = new MyQueue(5);
        queue.put("1");
        queue.put("2");
        queue.put("3");
        queue.put("4");
        queue.put("5");
        System.out.println("当前容器长度为:" + queue.getSize());
        Thread t1 = new Thread(() -> {
            queue.put("6");
            queue.put("7");
            // 队列满了,他将会一直等待
            queue.put("8");
        }, "t1");
        Thread t2 = new Thread(() -> {
            Object take1 = queue.take();
            Object take2 = queue.take();
            System.out.println("take1 = " + take1 + "====  take2  = " + take2);
        }, "t2");
        //测试极端情况,两秒钟后再执行另一个线程,会阻塞等待另一个线程take
        t1.start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

3.JDK中并发队列提供了两种实现,一种是高性能队列ConcurrentLinkedQueue,一种是阻塞队列BlockingQueue,两种都继承自Queue

(1)ConcurrentLinkedQueue

import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * @author QiuQiu&LL
 * @version 1.0
 */
public class ConcurrentLinkedQueueDemo {
    public static void main(String[] args) {
        ConcurrentLinkedQueue clq = new ConcurrentLinkedQueue();
        clq.add(1);
        clq.add(2);
        clq.add(3);
        clq.add(4);

        //  获取队头元素,并删除
        Object poll = clq.poll();
        System.out.println("poll = " + poll);
        // 获取队头元素,不删除
        Object peek = clq.peek();
        System.out.println("peek = " + peek);

        System.out.println("clq = " + clq);
    }
}

(2) BlockingQueue

  • blockingQueue主要有5中实现,具体如下
    image

ArrayBlockingQueue

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * @author QiuQiu&LL
 * @version 1.0
 */
public class ArrayBlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<Integer> abq = new ArrayBlockingQueue<>(2);
        // 添加一个元素
        abq.add(1);//add :添加元素,如果BlockingQueue可以容纳,则返回true,否则抛异常,支持添加集合
        boolean offerIsSuccess = abq.offer(2);//offer: 如果可能的话,添加元素,即如果BlockingQueue可以容纳,则返回true,否则返回false,支持设置超时时间
        System.out.println("offerIsSuccess = " + offerIsSuccess);
        //设置超时,如果超过时间就不添加,返回false,
        // abq.offer(3, 2, TimeUnit.SECONDS);
        //put 添加元素,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续
        // abq.put(4);
        new Thread(() -> {
            //poll 取走头部元素,若不能立即取出,则可以等time参数规定的时间,取不到时返回null,支持设置超时时间
            Integer poll = abq.poll();
            System.out.println("poll = " + poll);
        });
        abq.poll(2,TimeUnit.SECONDS);//两秒取不到返回null
        abq.offer(3, 2, TimeUnit.SECONDS);
        //take()  取走头部元素,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
        abq.take();
        //取出头部元素,但不删除
        abq.element();
        List drain=new ArrayList();
        //一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。
        int to = abq.drainTo(drain);
        System.out.println("to = " + to);
        System.out.println("drain = " + drain);
        List list=new ArrayList();
        abq.drainTo(list,2);//将队列中两个元素取到list中,取走后队列中就没有取走的元素
        System.out.println(list); //[a,b]
        System.out.println(abq);  //[]
    }
}

ArrayBlockingQueue

import java.util.concurrent.LinkedBlockingQueue;

/**
 * @author QiuQiu&LL
 * @version 1.0
 */
public class LinkedBlockingQueueDemo {
    public static void main(String[] args) {
        //可指定容量,也可不指定
        LinkedBlockingQueue<String> lbq = new LinkedBlockingQueue<>();
        lbq.add("a");
        lbq.add("b");
        lbq.add("c");
        //API与ArrayBlockingQueue相同
        //是否包含
        System.out.println(lbq.contains("a"));
        //移除头部元素或者指定元素  remove("a")
        System.out.println(lbq.remove());
        //转数组
        Object[] array = lbq.toArray();
        //element 取出头部元素,但不删除
        System.out.println(lbq.element());
        System.out.println(lbq.element());

        System.out.println("array = " + array);
    }
}

SynchronousQueue

import java.util.concurrent.SynchronousQueue;

/**
 * @author QiuQiu&LL
 * @version 1.0
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> sq=new SynchronousQueue<String>();
        // iterator() 永远返回空,因为里面没东西。
        // peek() 永远返回null
        /**
         * isEmpty()永远是true。
         * remainingCapacity() 永远是0。
         * remove()和removeAll() 永远是false。
         */
        new Thread(()->{
            try {
                //取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等。
                System.out.println(sq.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();
        new Thread(()->{
            try {
                //offer() 往queue里放一个element后立即返回,
                //如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false
                //true ,上面take线程一直在等,
                ////下面刚offer进去就被拿走了,返回true,如果offer线程先执行,则返回false
                System.out.println(sq.offer("b"));

            } catch (Exception e) {
                e.printStackTrace();
            }

        }).start();
        new Thread(()->{
            try {
                //往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走
                sq.put("a");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
}

DelayQueue

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

//网民
public class Netizen implements Delayed {
    //身份证
    private String ID;
    //名字
    private String name;
    //上网截止时间
    private long playTime;

    //比较优先级,时间最短的优先
    @Override
    public int compareTo(Delayed o) {
        Netizen netizen = (Netizen) o;
        return this.getDelay(TimeUnit.SECONDS) - o.getDelay(TimeUnit.SECONDS) > 0 ? 1 : 0;
    }

    public Netizen(String iD, String name, long playTime) {
        ID = iD;
        this.name = name;
        this.playTime = playTime;
    }

    //获取上网时长,即延时时长
    @Override
    public long getDelay(TimeUnit unit) {
        //上网截止时间减去现在当前时间=时长
        return this.playTime - System.currentTimeMillis();
    }

    public String getID() {
        return ID;
    }

    public void setID(String ID) {
        this.ID = ID;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getPlayTime() {
        return playTime;
    }

    public void setPlayTime(long playTime) {
        this.playTime = playTime;
    }
}
import java.util.concurrent.DelayQueue;

//网吧
public class InternetBar implements Runnable {
    //网民队列,使用延时队列
    private DelayQueue<Netizen> dq = new DelayQueue<Netizen>();

    //上网
    public void startPlay(String id, String name, Integer money) {
        //截止时间= 钱数*时间+当前时间(1块钱1秒)
        Netizen netizen = new Netizen(id, name, 1000 * money + System.currentTimeMillis());
        System.out.println(name + "开始上网计费......");
        dq.add(netizen);
    }

    //时间到下机
    public void endTime(Netizen netizen) {
        System.out.println(netizen.getName() + "余额用完,下机");
    }

    @Override
    public void run() {
        //线程,监控每个网民上网时长
        while (true) {
            try {
                //除非时间到.否则会一直等待,直到取出这个元素为止
                Netizen netizen = dq.take();
                endTime(netizen);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        //新建一个网吧
        InternetBar internetBar = new InternetBar();
        //来了三个网民上网
        internetBar.startPlay("001", "qiuqiu", 3);
        internetBar.startPlay("002", "ll", 7);
        internetBar.startPlay("003", "QIUQIU&LL", 5);
        Thread t1 = new Thread(internetBar);
        t1.start();
    }
}

4.案例1

import java.util.ArrayDeque;
import java.util.Queue;

/**
 * @author QiuQiu&LL
 * @version 1.0
 * 模拟银行办理业务员
 */
public class MyQueueDemo {
    public static void main(String[] args) {
        Queue<Request> que = new ArrayDeque<Request>();
        //模拟排队情况
        for (int i = 0; i < 10; i++) {
            final int num = i;
            que.offer(new Request() {
                //应用匿名内部类对象只能访问 final 修饰的变量
                @Override
                public void deposit() {
                    System.out.println("第" + num + "个人,办理存款业务,存款额度为:" + (Math.random() * 10000));
                }
            });
        }
        dealWith(que);
    }

    //处理业务
    public static void dealWith(Queue<Request> que) {
        Request req = null;
        while (null != (req = que.poll())) {
            req.deposit();
        }
    }
}

interface Request {
    //存款
    void deposit();
}

5.案例2

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 使用队列实现自定义堆栈
 * 1、弹
 * 2、压
 * 3、获取头
 */
public class MyStack<E> {
    //容器
    private Deque<E> container = new ArrayDeque<E>();
    //容量
    private int cap;

    public MyStack(int cap) {
        super();
        this.cap = cap;
    }

    //压栈
    public boolean push(E e) {
        if (container.size() + 1 > cap) {
            return false;
        }
        return container.offerLast(e);
    }

    //弹栈
    public E pop() {
        return container.pollLast();
    }

    //获取
    public E peek() {
        return container.peekLast();
    }

    public int size() {
        return this.container.size();
    }
}


//测试自定义堆栈
public class MyStockDemo {
    /**
     * @param args
     */
    public static void main(String[] args) {
        MyStack<String> backHistory = new MyStack<String>(3);
        backHistory.push("www.baidu.com");
        backHistory.push("www.google.com");
        backHistory.push("www.sina.com");
        backHistory.push("www.bjsxt.cn");
        System.out.println("大小:" + backHistory.size());

        //遍历
        String item = null;
        while (null != (item = backHistory.pop())) {
            System.out.println(item);
        }
    }
}

posted @ 2022-08-09 10:36  我也有梦想呀  阅读(42)  评论(0)    收藏  举报