常用的队列

队列对比
队列 场景 优点 缺点
ConcurrentLinkedQueue

1非阻塞线程安全最佳queue

2对全局的集合进行操作的场景

3一个适用于高并发场景下的队列,通过无锁的方式(CAS+volatile),实现了高并发下的高性能,通常ConcurrentLinkedQueue的性能好于BlockingQueue

原子操作效率高,无界队列
要特别注意到由于它的非阻塞性,并不像其他普通集合那样,获取队列的SIZE的方法并不是常量时间的花费,而是O(N)的,因此我们应该尽可能避免使用size()方法,可以考虑使用isEmpty()代替。
LinkedBlockingQueue 链表无界可阻塞线程安全队列 支持容量限制 效率相对集合队列低
DealyQueue
1缓存系统的设计

2定时任务调度(订单到期、限时支付)

可阻塞,可延迟 基于排序队列实现,效率很低
ArrayBlockingQueue
有界队列即初始化时指定的容量,就是队列最大的容量,不会出现扩容,容量满,则阻塞进队操作;容量空,则阻塞出队操作
可阻塞,出入队列效率高,同时能省内存 容量固定,不能扩容,出入队列不能同时进行,遍历元素更快

 

 

 

总结:

如果不需要阻塞队列,优先选择ConcurrentLinkedQueue;

如果需要阻塞队列,队列大小固定优先选择ArrayBlockingQueue,

队列大小不固定优先选择LinkedBlockingQueue;

如果需要对队列进行排序,选择PriorityBlockingQueue;

如果需要一个快速交换的队列,选择SynchronousQueue;

如果需要对队列中的元素进行延时操作,则选择DelayQueue

使用案列:

DelayQueue

使用DelayQueue实现延时任务非常简单,而且简便,全部都是标准的JDK代码实现,不用引入第三方依赖(不依赖redis实现、消息队列实现等),非常的轻量级。它的缺点就是所有的操作都是基于应用内存的,一旦出现应用单点故障,可能会造成延时任务数据的丢失。如果订单并发量非常大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的风险。所以使用DelayQueue实现延时任务,只适用于任务量较小的情况。

package org.linlinjava.litemall.core.task;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;import java.util.concurrent.DelayQueue;import java.util.concurrent.Executors;@Componentpublic class TaskService {    private TaskService taskService;    private DelayQueue delayQueue =  new DelayQueue();    @PostConstruct    private void init() {        taskService = this;        Executors.newSingleThreadExecutor().execute(new Runnable() {            @Override            public void run() {                while (true) {                    try {                        Task task = delayQueue.take();                        task.run();                    } catch (Exception e) {                        e.printStackTrace();                    }                }            }        });    }    public void addTask(Task task){        if(delayQueue.contains(task)){            return;        }        delayQueue.add(task);    }    public void removeTask(Task task){        delayQueue.remove(task);    }}package org.linlinjava.litemall.core.task;import com.google.common.primitives.Ints;import java.time.LocalDateTime;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public abstract class Task implements Delayed, Runnable{    private String id = "";    private long start = 0;    public Task(String id, long delayInMilliseconds){        this.id = id;        this.start = System.currentTimeMillis() + delayInMilliseconds;    }    public String getId() {        return id;    }    @Override    public long getDelay(TimeUnit unit) {        long diff = this.start - System.currentTimeMillis();        return unit.convert(diff, TimeUnit.MILLISECONDS);    }    @Override    public int compareTo(Delayed o) {        return Ints.saturatedCast(this.start - ((Task) o).start);    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null) return false;        if (!(o instanceof Task)) {            return false;        }        Task t = (Task)o;        return this.id.equals(t.getId());    }    @Override    public int hashCode() {        return this.id.hashCode();    }}




package org.jeecg.modules.Thread.queue.delayed;


import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;


import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;


/**
* DelayQueueCase:
*         1 实现延迟获取元素的无界队列无界阻塞队列,其中添加进该队列的元素必须实现Delayed接口(指定延迟时间),
*                而且只有在延迟期满后才能从中提取元素;
*         2 DelayQueue内部使用非线程安全的优先队列(PriorityQueue)
*
* DelayQueue使用场景:
*    1缓存系统的设计:一旦能从DelayQueue中获取元素时,表示缓存有效期到了
*    2定时任务调度(订单到期、限时支付):一旦从DelayQueue中获取到任务就开始执行
*    用DelayQueue解决这种需求是比较优雅的解决方案(不优雅的解决方案就是使用定时器进行定时轮询)。
* 使用DelayQueue实现延时任务非常简单,而且简便,全部都是标准的JDK代码实现,不用引入第三方依赖(不依赖redis实现、消息队列实现等),非常的轻量级。它的缺点就是所有的操作都是基于应用内存的,
* 一旦出现应用单点故障,可能会造成延时任务数据的丢失。如果订单并发量非常大,因为DelayQueue是无界的,订单量越大,队列内的对象就越多,可能造成OOM的风险。所以使用DelayQueue实现延时任务,
* 只适用于任务量较小的情况。
*/
@Slf4j
public class DelayQueueCase {
    //声明延迟阻塞队列
    private static DelayQueue delayQueue=new DelayQueue<>();
    public static void main(String[] args) {
        //消息队列
//        ExecutorService executorService = Executors.newFixedThreadPool(2);
//        executorService.execute(new DelayQueueProduct());
//        executorService.execute(new DelayQueueConsumer());
        //缓存
        // TODO Auto-generated method stub
        CacheUtils cache = new CacheUtils();
        cache.put("No1", "Value of No1", 1, TimeUnit.MILLISECONDS);
        System.out.println(cache.get("No1"));
        // Wait 1001 milliseconds
        try {
            TimeUnit.MILLISECONDS.sleep(1001);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // Get the value, it will be null
        System.out.println(cache.get("No1"));


    }


//-----------------------------------------------DelayQueue实现消息队列------------------------------------------------------------//
    /**
     * 生产者
     */
    public static class DelayQueueProduct implements Runnable{
        @SneakyThrows
        @Override
        public void run() {
            for (int i = 0; i <20 ; i++) {
               String data="缓存数据"+i;
               //设置10s后过期
                MyDelay myDelay=new MyDelay(20*1000,data);
                delayQueue.put(myDelay);
                log.info("插入缓存:{}",data);
               // TimeUnit.SECONDS.sleep(1);
            }
        }
    }


    public static class DelayQueueConsumer implements Runnable{
        @SneakyThrows
        @Override
        public void run() {
           while (!Thread.currentThread().isInterrupted()){
               MyDelay data=delayQueue.take();
               TimeUnit.SECONDS.sleep(5);
               if(null!=data){
                   log.info("数据过期了:{}",data);
               }
           }
        }
    }


    /**
     * 实体
     * @param 
     */
    static class MyDelay implements Delayed {




        long delayTime; // 延迟时间
        long expire; // 过期时间
        T data;


        public MyDelay(long delayTime, T t) {
            this.delayTime = delayTime;
            // 过期时间 = 当前时间 + 延迟时间
            this.expire = System.currentTimeMillis() + delayTime;
            data = t;
        }


        /**
         * 剩余时间 = 到期时间 - 当前时间
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }


        /**
         * 优先级规则:两个任务比较,时间短的优先执行
         */
        @Override
        public int compareTo(Delayed o) {
            long f = this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS);
            int result=(f==0)?0:((f<0)?-1:1);
            return result;
        }




        @Override
        public String toString() {
            return "delayTime=" + delayTime +
                    ", expire=" + expire +
                    ", data=" + data;
        }
    }
//-----------------------------------------------DelayQueue实现缓存设计------------------------------------------------------------//
   public static class CacheUtils{
    private  DelayQueue>> queue=new DelayQueue>>();
    private ConcurrentHashMap>> data=new ConcurrentHashMap>>();
    public CacheUtils() {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()){
                    try {
                        timeoutCheck();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        },"TimeoutChecker");
        //设置守护线程
        t.setDaemon(true);
        t.start();
    }


    private  void timeoutCheck() throws InterruptedException {
        DelayCacheBean> item = queue.take();
        if (item != null) {
            data.remove(item.getItem().getKey());
        }
    }
    public void put(K key, V value, long timeout, TimeUnit unit) {
        // If key already exists, remove from queue
        if (data.containsKey(key)) {
            queue.remove(data.get(key));
        }
        Cache pair = new Cache(key, value);
        DelayCacheBean> item = new DelayCacheBean>(pair,TimeUnit.MILLISECONDS.convert(timeout, unit));
        data.put(key, item);
        queue.put(item);
    }
    public void remove(K key) {
        queue.remove(data.remove(key));
    }
    public V get(K key) {
        if (data.get(key) != null) {
            return data.get(key).getItem().getValue();
        }
        return null;
    }
}


    /**
     * 键值对数据结构
     * @param 
     * @param 
     */
  public static class Cache{
        private K key;
        private V value;


      public Cache(K key, V value) {
          this.key = key;
          this.value = value;
      }


      public K getKey() {
          return key;
      }


      public void setKey(K key) {
          this.key = key;
      }


      public V getValue() {
          return value;
      }


      public void setValue(V value) {
          this.value = value;
      }
  }


    /**
     * 存放cache键值对
     * @param 
     */
public static class DelayCacheBean implements Delayed{
     private T item;
     private long expire;
     private long sequence;
     private  AtomicLong sequencer = new AtomicLong(0);




    public DelayCacheBean(T item, long expire) {
        this.item = item;
        this.expire = System.currentTimeMillis() + expire;
        this.sequence =  sequencer.getAndIncrement();
    }
      public T getItem(){
        return item;
    }


    @Override
     public long getDelay(@NotNull TimeUnit unit) {
        return unit.convert(this.expire - System.currentTimeMillis(), TimeUnit.MILLISECONDS);


     }


     @Override
     public int compareTo(@NotNull Delayed o) {
         if (this == o)
             return 0;
         if (o instanceof DelayCacheBean) {
             DelayCacheBean other = (DelayCacheBean) o;
             long dist = this.expire - other.expire;
             if (dist > 0)
                 return 1;
             if (dist < 0)
                 return -1;
             // If the expire time is same, compare the sequence
             return this.sequence < other.sequence ? -1 : 1;
         }
         // If the specified object is not a instance of DelayItem, compare
         // the return value of getDelay()
         long dist = this.getDelay(TimeUnit.MILLISECONDS)
                 - o.getDelay(TimeUnit.MILLISECONDS);
         return dist > 0 ? 1 : (dist < 0 ? -1 : 0);
     }
}
}

 

package org.linlinjava.litemall.core.task;


import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;


@Component
public class TaskService {
    private TaskService taskService;
    private DelayQueue delayQueue =  new DelayQueue();


    @PostConstruct
    private void init() {
        taskService = this;


        Executors.newSingleThreadExecutor().execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Task task = delayQueue.take();
                        task.run();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }


    public void addTask(Task task){
        if(delayQueue.contains(task)){
            return;
        }
        delayQueue.add(task);
    }


    public void removeTask(Task task){
        delayQueue.remove(task);
    }


}




package org.linlinjava.litemall.core.task;


import com.google.common.primitives.Ints;


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


public abstract class Task implements Delayed, Runnable{
    private String id = "";
    private long start = 0;


    public Task(String id, long delayInMilliseconds){
        this.id = id;
        this.start = System.currentTimeMillis() + delayInMilliseconds;
    }


    public String getId() {
        return id;
    }


    @Override
    public long getDelay(TimeUnit unit) {
        long diff = this.start - System.currentTimeMillis();
        return unit.convert(diff, TimeUnit.MILLISECONDS);
    }


    @Override
    public int compareTo(Delayed o) {
        return Ints.saturatedCast(this.start - ((Task) o).start);
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null) return false;
        if (!(o instanceof Task)) {
            return false;
        }
        Task t = (Task)o;
        return this.id.equals(t.getId());
    }


    @Override
    public int hashCode() {
        return this.id.hashCode();
    }
}

ArrayBlockingQueue



package org.jeecg.modules.Thread.queue.array;


import lombok.extern.slf4j.Slf4j;


import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


/**
* ArrayBlockingQueue是一个阻塞式/有界的队列
* 使用场景
*     有界队列即初始化时指定的容量,就是队列最大的容量,不会出现扩容,容量满,则阻塞进队操作;容量空,则阻塞出队操作
*     队列不支持空元素
* 先进先出队列
* 入队(生产者)
*    offer:队列满了丢弃。 return false;
*    add :队列满了报错  throw new IllegalStateException("Queue full");
*    put :阻塞。 final ReentrantLock lock = this.lock;
* 出队(消费者)
*    poll :如果队列为空则返回null。 return (count == 0) ? null : dequeue();
*    take :阻塞    notEmpty.await();
*    remove:报错   throw new NoSuchElementException();
*/
@Slf4j
public class ArrayBlockingQueueCase {
    public static void main(String[] args) {
        //通过 ExecutorService 启动 3 个线程,2 两个生产者,1 个消费者
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(5);


        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 最多生产 5 个数据
        AtomicInteger numberOfElementsToProduce = new AtomicInteger(5);


        // 2 个生产者线程
        executorService.submit(new ArrayBlockingQueueProducer(arrayBlockingQueue, numberOfElementsToProduce));
        executorService.submit(new ArrayBlockingQueueProducer(arrayBlockingQueue, numberOfElementsToProduce));
        // 1 个消费者线程
        executorService.submit(new ArrayBlockQueueConsumer(arrayBlockingQueue, numberOfElementsToProduce));
        log.info("开始等待");
        executorService.shutdown();
        waitUntilTerminate(executorService, 10);
    }


    public static void waitUntilTerminate(final ExecutorService executorService, final int timeout) {
        try {
            log.info("等待完毕");
            //当使用awaitTermination时,主线程会处于一种等待的状态,等待线程池中所有的线程都运行完毕后才继续运行。
            //如果等待的时间超过指定的时间,但是线程池中的线程运行完毕,那么awaitTermination()返回true。执行分线程已结束。
            //如果等待的时间超过指定的时间,但是线程池中的线程未运行完毕,那么awaitTermination()返回false。不执行分线程已结束。
            //如果等待时间没有超过指定时间,等待!
            if (executorService.awaitTermination(timeout, TimeUnit.SECONDS)) { //超时后直接关闭
                log.info("分线程已经结束");
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) { //awaitTermination 出现中断异常也将触发关闭
            executorService.shutdownNow();
        }
    }


    /**
     * 消费者
     */
    public static class ArrayBlockQueueConsumer implements Runnable {
        //容量
        private ArrayBlockingQueue queue;
        //生产者指定的数量
        private AtomicInteger numberOfElenmentsToProduce;


        public ArrayBlockQueueConsumer(ArrayBlockingQueue queue, AtomicInteger numberOfElenmentsToProduce) {
            this.queue = queue;
            this.numberOfElenmentsToProduce = numberOfElenmentsToProduce;


        }


        @Override
        public void run() {
            try {
                while (!queue.isEmpty()|| numberOfElenmentsToProduce.get() >= 0) {
                    try {
                        //从队列中获取任务,并执行任务
                        String task = queue.take();//阻塞
                        log.info("thead:{},消费者 task:{},队列:{},队列容量:{}", Thread.currentThread().getName(), task,queue.isEmpty(),numberOfElenmentsToProduce.get());
                        //队列中数据为空,消费者线程退出,这边由bug消费者没有消费就退出了,需要等待加上numberOfElenmentsToProduce=0
                        if (queue.isEmpty()&&numberOfElenmentsToProduce.get()==0) {
                            break;
                        }
                    } catch (Exception e) {
                        log.error("出错了:{}", e.getMessage());
                    }
                }


            } catch (Exception e) {
                log.error(this.getClass().getName().concat(". has error"), e);
            }
        }
    }


    /**
     * 生产者线程向容器存入指定总量的 任务
     */
    public static class ArrayBlockingQueueProducer implements Runnable {
        //容量
        private ArrayBlockingQueue queue;
        //生产者指定的数量
        private AtomicInteger numberOfElenmentsToProduce;


        public ArrayBlockingQueueProducer(ArrayBlockingQueue queue, AtomicInteger numberOfElenmentsToProduce) {
            this.queue = queue;
            this.numberOfElenmentsToProduce = numberOfElenmentsToProduce;


        }


        @Override
        public void run() {
            try {
                while (numberOfElenmentsToProduce.get() > 0) {
                    try {
                        //向队列中添加任务
                        String task = String.format("task_%s", numberOfElenmentsToProduce.getAndUpdate(x ->
                                x - 1
                        ));
                        //阻塞
                        queue.put(task);
                        log.info("thead:{},生产者 task:{}", Thread.currentThread().getName(), task);
                        //任务为0,生产者线程退出
                        if (numberOfElenmentsToProduce.get() == 0) {
                            break;
                        }
                    } catch (Exception e) {
                        log.error("出错了:{}", e.getMessage());
                    }
                }


            } catch (Exception e) {
                log.error(this.getClass().getName().concat(". has error"), e);
            }
        }
    }


}

你可能感兴趣的:(java,队列)