Understanding of Queue

Concurrent Queue

On which concurrent queue jdk provides two sets of implementations, one is a high-performance queue represented by ConcurrentLinkedQueue, and the other is a blocking queue represented by BlockingQueue, both of which are inherited from Queue

1.ConcurrentLinkedQueue

是一个使用与高并发场景下的队列,通过无锁的方式,实现类高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它是基于链接点无界
线程安全队列.该队列的元素遵循先进先出的原则.头是加入的,尾是最近加入的,该队列不允许null值

这个队列使用链表作为其数据结构。这个类算是高并发环境中性能最好的队列。高性能是因为其内部复杂的实现。

Important methods of ConcurrentLinkedQueue:

add()和offer()都是加入元素的方法(在ConcurrentLinkedQueue,这两个方法没有任何区别)
poll()和peak()都是取头元素,区别在于前者会删除元素后者不会.

2. BlockingQueue interface

ArrayBlockingQueue:

    基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长的数组,一遍缓存队列中的数据对象,对内部没实现读写分离,也就意味着生产和消费
    不能完全并行,长度是需要定义的,可以指定现进先出,或者,后进后出,也叫有界队列,在很多场合非常实用,

Left BlockingQueue:

    基于链表的阻塞队列,同ArrayBlockingQueue类似,器内部也维护这一个数据缓存队列(该队列由一个链表构成),LinkedBlockingQueue之所以能够高效地
    处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者操作的完全并行运行.它是一个无界队列

SynchronousQueue:

    一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并消费.

See a concrete example:

     SynchronousQueue<String> sQueue = new SynchronousQueue<String>();
     sQueue.add("aaa");

If the program is written like this, it will definitely report an error, because elements cannot be placed in the SynchronousQueue:

    报的错:
    Exception in thread "main" java.lang.IllegalStateException: Queue full
        at java.util.AbstractQueue.add(AbstractQueue.java:98)
    at com.threadbasic015.SynchronousQueueDemo.main(SynchronousQueueDemo.java:8)

But it's not that SynchronousQueue can't use the add() method;

    final SynchronousQueue<String> q = new SynchronousQueue<String>();
    Thread t1 = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                System.out.println(q.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    t1.start();

    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            q.add("aaa");
        }
    });
    t2.start();

这端代码是没有问题的,一个线程从SyhchronsQueue拿元素,一个线程往SynchronousQueue队列里面放元素,但是需要注意的是,add()方法,放的元素并不是
放在这个SynchronousQueue队列里面,而是直接拿给了take()方法去获取,如果没有take()方法,那么add()方法肯定是有问题的.

PriorityBlockingQueue:

    基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现Comparable接口),在实现
    PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,他是一个无界的队列
这个队列是的元素是有优先级的,队列的元素要实现Comparaable接口,自定义比较的方法,
例子:

    public class Task implements Comparable<Task> {

        private int id;
        private String name;

        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }

        @Override
        public int compareTo(Task task) {
            System.out.println("this:" + this);
            System.out.println("task:" + task);
            System.out.println("===============");
            return this.id > task.id ? 1 : (this.id < task.id ? -1 : 0);
        }

        @Override
        public String toString() {
            return "Task [id=" + id + ", name=" + name + "]";
        }
    }

public class UsePriorityBlockingQueue {


    public static void main(String[] args) throws InterruptedException {

        PriorityBlockingQueue<Task> q = new PriorityBlockingQueue<>();

        Task t1 = new Task();
        t1.setName("第一个元素");
        t1.setId(3);
        ///////
        Task t2 = new Task();
        t2.setName("第二个元素");
        t2.setId(2);
        ///////
        Task t3 = new Task();
        t3.setName("第三个元素");
        t3.setId(1);
        //////
        Task t4 = new Task();
        t4.setName("第四个元素");
        t4.setId(5);
        //////
        Task t5 = new Task();
        t5.setName("第五个元素");
        t5.setId(4);
        q.add(t1);
        q.add(t2);
        q.add(t3);
        q.add(t4);
        q.add(t5);

        System.out.println(q);
        Task take = q.take();
        System.out.println(take);
        System.out.println(q);
    }
}

The take method goes back to find the element with the highest priority

>
Elements are put into the queue, and there is no order at first. When the take() method of the queue is called to fetch data, the elements in the queue are compared once. Note that there is no sorting in it. Once the take() method is called, the optimal
The highest priority element is taken out

DelayQueue:

    带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能够从队列中获取元素.DelayQueue中元素必须实现Delayed接口,DelayQueue是一个
    没有大小限制的队列,应用场景很多,比如对缓存超时的数据进行移除,任务超时处理,空闲连接的关闭等待.

Now simulate an example:

    网吧上网的例子,假设网吧开始营业,路人甲来上网交了1块钱,上了1小时,过了一段时间路人乙也来上网了,交了10块钱,上了10小时,过了一段时间路人丙来了,
    交了5块钱,上了5小时.上网的时间跟交的钱成正比,这个事件相当于是队列中的延迟时间

Implementation: Define a screen name class (WangMing) that implements the Delayed interface. This class should be placed in the DelayQueue queue:

    public class WangMing implements Delayed {

        private String name;

        private String id; //身份证号

        private Long endTime; // 截止时间

        private TimeUnit timeUnit = TimeUnit.SECONDS;

        public WangMing(String name, String id, Long endTime) {
            this.name = name;
            this.id = id;
            this.endTime = endTime;
        }   
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getId() {
            return id;
        }
        public void setId(String id) {
            this.id = id;
        }
        public Long getEndTime() {
            return endTime;
        }
        public void setEndTime(Long endTime) {
            this.endTime = endTime;
        }

        /**
         * 用来判断是到了延迟时间
         * 返回值:剩余延迟时间;零或负值指示延迟时间已经用尽
         */
        @Override
        public long getDelay(TimeUnit unit) {
            System.out.println(this.getName() + " -当前时间:" + System.currentTimeMillis());
            return endTime - System.currentTimeMillis();
        }

        /**
         *相互比较,进行排序
         */
        @Override
        public int compareTo(Delayed delayed) {
            WangMing wangMing = (WangMing) delayed;

            return this.getDelay(timeUnit) - delayed.getDelay(timeUnit) > 0 ? 1:0;
        }   
}

Define the Internet Cafe (WangBa) class:

    public class WangBa implements Runnable {
        private DelayQueue<WangMing> queue = new DelayQueue<WangMing>();

        private boolean yingye  = true;

        public void shangji(String name, String id, int money) {
            WangMing man = new WangMing(name, id, 1000*money + System.currentTimeMillis());
            System.out.println("网民:" + name +"开始上机了  - " + man.getEndTime() +"后下机");
            this.queue.add(man);
        }
        public void xiaxian(WangMing man) {
            System.out.println(man.getName() + "下线了");
        }
        @Override
        public void run() {
            while(yingye) {
                try {
                    WangMing wangMing = queue.take();
                    xiaxian(wangMing);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) {
            try {
                WangBa wangBa = new WangBa();
                System.out.println("网吧开始营业了");
                Thread shangwang = new Thread(wangBa);
                shangwang.start();
                wangBa.shangji("路人甲", "123", 2);
                wangBa.shangji("路人乙", "234", 5);
                wangBa.shangji("路人丙", "345", 3);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

analyze:

网吧类实现了Runnable接口,它的run方法,是要去DelaeyQuee队列中拿元素,如果,能够拿到元素,说明里面有元素的延迟时间已经用完了,就要下机.如果拿不到
元素,就一直在等着(这是一个死循环),

WangMing实现了Delayed接口:有连个必须要实现的方法
1.getDelay()这个方法,只要这个元素没有超过延迟时间,就要一直运行这个方法,因为要通过这个方法来不断进行判断,是否过了延迟时间
2.comparedTo()这个方法,是DelayQueue内部元素需要实现排序要用的比较的方法

>
Print result:
Internet cafes are open
Netizens : Passerby A starts to get on the plane - 1524627051663 and then gets off the plane
Netizens: Passerby B starts to get on the plane - 1524627058664 then gets off the plane
Netizens : Passerby C starts to get on the plane - 1524627056664 and then get off the plane
Passerby A gets off
Passerby C is offline
Passerby B is offline

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325774891&siteId=291194637