你知道阻塞队列有哪些吗?

什么是阻塞队列

阻塞队列也是Queue队列的一种,叫BlockingQueue,所以BlockingQueue继承了 Queue。另外Queue 和 BlockingQueue 都是在 Java 5 中加入的。

public interface BlockingQueue<E> extends Queue<E> {

BlockingQueue是线程安全的,所以很多时候我们可以利用这个特性,去解决业务中的问题,比如在使用生产者/消费者模式的时候,生产者只需要往队列里添加元素,消费者只需要从队列里取出它们就可以了。

阻塞队列关键的两个方法在于put 和 take,put的作用是插入元素,但是当队列满了的时候,不会抛出异常,也不会返回false,而是让插入的线程处于等待状态,直到队列有空闲空间,当其他线程调用take的时候,此时队列就会释放之前处于阻塞的线程,并把刚才那个元素添加进去。

所以take 方法的作用是获取并移除队列的头结点,但是如果队列中没有数据时,他会阻塞,直到队列中有数据。一旦有了,就会立刻解除阻塞状态,并且取到数据。

常见的阻塞队列

ArrayBlockingQueue

ArrayBlockingQueue 是最典型的有界队列,内部是用数组存储元素的,利用 ReentrantLock实现线程安全。

我们创建他时,需要指明容量,拿第二个构造方法来说,第一个参数是容量,第二个参数是是否公平,如果设置为公平的话,那么等待时间最长的线程会优先被处理,如果设置为非公平的,那么就存在插队的可能,但是公平策略同时会带来一定的性能损耗。

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

示例:

public class BlockingDemo {
    public static void main(String[] args) throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue =new ArrayBlockingQueue(3);

        new Thread(() -> {
            for (int i = 0; i < 6; i++) {
                try {
                    arrayBlockingQueue.put(i+"");
                    System.out.println("put");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(1000);
        new Thread(()->{
            while (true){
                String take = null;
                try {
                    take = arrayBlockingQueue.take();
                    System.out.println("取出:"+take);

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}

put
put
put
取出:0
put
取出:1
put
取出:2
put
取出:3
取出:4
取出:5
LinkedBlockingQueue

LinkedBlockingQueue内部用链表实现的。如果创建时不指定它的初始容量,那么它容量默认就为整型的最大值 Integer.MAX_VALUE,由于这个数非常大,我们通常不可能放入这么多的数据,所以 LinkedBlockingQueue 也被称作无界队列,代表它几乎没有界限。

 public LinkedBlockingQueue() {
     this(Integer.MAX_VALUE);
 }

示例:

public class BlockingDemo {
    public static void main(String[] args) throws InterruptedException {
        LinkedBlockingQueue<String> blockingQueue =new LinkedBlockingQueue();

        new Thread(() -> {
            for (int i = 0; i < 6; i++) {
                try {
                    blockingQueue.put(i+"");
                    System.out.println("put");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(1000);
        new Thread(()->{
            while (true){
                String take = null;
                try {
                    take = blockingQueue.take();
                    System.out.println("取出:"+take);

                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}
put
put
put
put
put
put
取出:0
取出:1
取出:2
取出:3
取出:4
取出:5

SynchronousQueue

SynchronousQueue 最大的不同之处在于,它的容量为 0,所以没有一个地方来暂存元素,导致每次取数据都要先阻塞,直到有数据被放入,相反,每次放数据的时候也会阻塞,直到有消费者来取。

 public SynchronousQueue() {
     this(false);
 }

 public SynchronousQueue(boolean fair) {
     transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
 }

示例交替打印

public class BlockingDemo {
    public static void main(String[] args) throws InterruptedException {
        String[] str1={"a","b","c","d","e","f"};
        String[] str2={"1","2","3","4","5","6"};
        SynchronousQueue<String> blockingQueue =new SynchronousQueue();



        new Thread(()->{
            for (int i = 0; i < str1.length; i++) {
                try {
                    blockingQueue.put(str1[i]);
                    System.out.print(blockingQueue.take());

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < str1.length; i++) {
                try {
                    System.out.print(blockingQueue.take());
                    blockingQueue.put(str2[i]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}
输出:a1b2c3d4e5f6
PriorityBlockingQueue

PriorityBlockingQueue 是一个支持优先级的无界阻塞队列,可以通过自定义类实现 compareTo() 方法来指定元素排序规则,或者初始化时通过构造器参数 Comparator 来指定排序规则。同时,插入队列的对象必须是可比较大小的,也就是 Comparable 的,否则会抛出 ClassCastException 异常。

public class BlockingDemo {
    public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue blockingQueue =new PriorityBlockingQueue();


        blockingQueue.put(new Dog(1));
        blockingQueue.put(new Dog(10));
        blockingQueue.put(new Dog(8));
        blockingQueue.put(new Dog(2));


        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());

    }

    static class Dog implements  Comparable<Dog>{
        private int age;


        public Dog(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return age+"";
        }

        @Override
        public int compareTo(Dog o) {
            return o.age-age;
        }
    }
}

10
8
2
1
DelayQueue

DelayQueue是一个具有“延迟”功能的队列,这种队列是有序的,越靠近队列头代表越早过期。DelayQueue用于放置实现了Delayed接口的对象,Delayed 接口继承自 Comparable,里面的getDelay需要我们实现,getDelay 方法返回的是“还剩下多长的延迟时间才会被执行”,如果返回 0 或者负数则代表任务已过期,才能从队列中取走。

DelayQueue中内部使用的是PriorityQueue存放数据,使用ReentrantLock实现线程同步。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
    implements BlockingQueue<E> {
    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue<E> q = new PriorityQueue<E>();
}

示例:

public class MyData implements Delayed {
    private  long expire;
    private int id;
    private long start;

    public MyData(int  seconds) {
        this.expire = System.currentTimeMillis()+TimeUnit.SECONDS.toMillis(seconds);
        this.start=System.currentTimeMillis();
        this.id = seconds;
    }


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

    @Override
    public int compareTo(Delayed o) {
        return (int)(this.getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS));
    }

    @Override
    public String toString() {
        return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()-this.start) +"秒";
    }
}



 public static void main(String[] args) throws IOException, InterruptedException {
     DelayQueue<MyData> queue =new DelayQueue<>();
     for (int i = 1; i <=5 ; i++) {
         queue.offer(new MyData(i));
     }
     for (;;){
         System.out.println(queue.take());
     }
 }
 
12345
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值