java concurrent 包->BlockingQueue

一、java.util.concurrent 包下的类分类图,了解一下。

Alt

一:collections部分

  • 1、BlockingQueue为接口,实现该接口的子类有:
实现子类类型
ArrayBlockingQueue实现类
DelayQueue实现类
LinkedBlockingQueue实现类
SynchronousQueue实现类
PriorityBlockingQueue实现类
TransferQueue接口
  • 2、BlockingQueue 用法,BlockingQueue 通常用于一个线程生产对象,而另外一个线程消费这些对象的场景
    Alt

  • 3、BlockingQueue (API用法)

    • 如果队列到达了容纳点,例如,队列已经满了,再去put的时候,会遇到阻塞的情况,如果队列为空,如果take的时候,则会遇到阻塞的情况。直到满足条件。
      Alt
      • 抛异常:如果试图的操作无法立即执行,抛一个异常。
      • 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。。
      • 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
      • 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是 true / false)。
  • 4、BlockingQueue 代码案例(ArrayBlockingQueue 用法)

    • 无法向一个 BlockingQueue 中插入 null
    • ArrayBlockingQueue 类实现了 BlockingQueue 接口。
    • ArrayBlockingQueue 是一个有界的阻塞队列,其内部实现是将对象放到一个数组里。有界也就意味着,它不能够存储无限多数量的元素。它有一个同一时间能够存储元素数量的上限。
    • 你可以在对其初始化的时候设定这个上限,但之后就无法对这个上限进行修改了(译者注:因为它是基于数组实现的,也就具有数组的特性:一旦初始化,大小就无法修改)。
    • ArrayBlockingQueue 内部以 FIFO(先进先出)的顺序对元素进行存储。队列中的头元素在所有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
 public static void main(String[] args) throws Throwable {
        BlockingQueue queue = new ArrayBlockingQueue(1024);

        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);

        new Thread(producer).start();
        new Thread(consumer).start();

        Thread.sleep(4000);
    }

    public static class Producer implements Runnable{
        protected BlockingQueue queue = null;

        public Producer(BlockingQueue queue) {
            this.queue = queue;
        }
        @Override
        public void run() {
            try {
                Thread.sleep(7000);
                queue.put("1"); //无需考虑安全问题 直接使用
                Thread.sleep(1000);
                queue.put("2");
                Thread.sleep(1000);
                queue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

   public static class Consumer implements Runnable{

        protected BlockingQueue queue = null;

        public Consumer(BlockingQueue queue) {
            this.queue = queue;
        }

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

  • 5、BlockingQueue 代码案例(延迟队列 DelayQueue 用法)

    • 是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期时间最长。注意:不能将null元素放置到这种队列中。
    • Delayed,一种混合风格的接口,用来标记那些应该在给定延迟时间之后执行的对象。此接口的实现必须定义一个 compareTo 方法,该方法提供与此接口的 getDelay 方法一致的排序。
    • 代码案例:(需要注意的是:compareTo的排序规则很重要,队列会按照排序的顺序去获取数据,如果说排序的规则和到期时间的规则不一致,则会导致队首的元素还没到期,但是队内的元素已经到期了,但是队内的元素拿不出来。直达队首的元素到期能获取到之后,对内的元素才会释放)

    >>>>>>>第一消息实体

class Message implements Delayed {

    private int id;
    private String body; // 消息内容
    private long excuteTime;// 延迟时长,这个是必须的属性因为要按照这个判断延时时长。

    public int getId() {
        return id;
    }

    public String getBody() {
        return body;
    }

    public long getExcuteTime() {
        return excuteTime;
    }

    public Message(int id, String body, long delayTime) {
        this.id = id;
        this.body = body;
        this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
    }

      @Override
      public String toString() {
          return "Message{" +
                  "id=" + id +
                  ", body='" + body + '\'' +
                  ", excuteTime=" + excuteTime +
                  '}';
      }

      @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);
    }

    @Override
    public int compareTo(Delayed delayed) {
        Message msg = (Message) delayed;
        return Long.valueOf(this.excuteTime) > Long.valueOf(msg.excuteTime) ? 1
                : (Long.valueOf(this.excuteTime) < Long.valueOf(msg.excuteTime) ? -1 : 0);
    }
}

>>>>>>>第二消费消息的消费者

class Consumer implements Runnable {
    // 延时队列 ,消费者从其中获取消息进行消费
    private DelayQueue<Message> queue;

    public Consumer(DelayQueue<Message> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Message take = queue.take();
                System.out.println("消费消息id:" + take.getId() + " 消息体:" + take.getBody());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

>>>>>>第三存放消息的延时队列

 public static void main(String[] args) throws Throwable {
// 创建延时队列
        DelayQueue<Message> queue = new DelayQueue<Message>();
        // 添加延时消息,m1 延时3s
        Message m2 = new Message(2, "hello", 10000);
        Message m1 = new Message(1, "world", 5000);
        // 添加延时消息,m2 延时10s
        //将延时消息放到延时队列中
        queue.offer(m2);
        queue.offer(m1);
        System.out.println(queue.peek().toString());
        // 启动消费线程 消费添加到延时队列中的消息,前提是任务到了延期时间
        ExecutorService exec = Executors.newFixedThreadPool(1);
        exec.execute(new Consumer(queue));
        exec.shutdown();

        //控制台输出
//        Message{id=1, body='world', excuteTime=2389652621500816}
//        消费消息id:1 消息体:world
//        消费消息id:2 消息体:hello
    }

  • 6、 BlockingQueue 代码案例( LinkedBlockingQueue用法)
class LinkedBlockingQueueTest {
    //LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(3);
    private int i;
    LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(3);

    class Producer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    queue.put(i++);
                    //System.out.print("生产,,,,,,");
                    System.out.println("生产,,,,,,剩余容量:" + queue.remainingCapacity());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
//                    Thread.sleep(1000);
                    queue.take();
                    System.out.println("消费,,,,剩余容量:" + queue.remainingCapacity());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
   LinkedBlockingQueueTest linkedBlockingQueueTest=new LinkedBlockingQueueTest();
        new Thread(linkedBlockingQueueTest.new Producer()).start();
        new Thread(linkedBlockingQueueTest.new Consumer()).start();
  • 7、 BlockingQueue 代码案例( SynchronousQueue用法)
    • 一:不像ArrayBlockingQueue或LinkedListBlockingQueue,SynchronousQueue内部并没有 数据缓存空间,你不能调用peek()方法来看队列中是否有数据元素,因为数据元素只有当你试着取走的时候才可能存在,不取走而只想偷窥一下是不行的,当然遍历这个队列的操作也是不允许的。队列头元素是第一个排队要插入数据的线程,而不是要交换的数据。数据是在配对的生产者和消费者线程之间直接传递的,并不会将数据缓冲数据到队列中。可以这样来理解:生产者和消费者互相等待对方,握手,然后一起离开。
    • 不能在同步队列上进行 peek,因为仅在试图要取得元素时,该元素才存在;
    • **除非另一个线程试图移除某个元素,否则也不能(使用任何方法)添加元素;也不能迭代队列,因为其中没有元素可用于迭代。队列的头是尝试添加到队列中的首个已排队线程元素; 如果没有已排队线程,则不添加元素并且头为 null。 **
    • 对于其他 Collection 方法(例如 contains),SynchronousQueue 作为一个空集合。此队列不允许 null 元素。
    • 它非常适合于传递性设计,在这种设计中,在一个线程中运行的对象要将某些信息、事件或任务传递给在另一个线程中运行的对象,它就必须与该对象同步。
    • 对于正在等待的生产者和使用者线程而言,此类支持可选的公平排序策略。默认情况下不保证这种排序。 但是,使用公平设置为 true 所构造的队列可保证线程以 FIFO 的顺序进行访问。 公平通常会降低吞吐量,但是可以减小可变性并避免得不到服务。
    • SynchronousQueue的以下方法:
      • iterator() 永远返回空,因为里面没东西
      • peek() 永远返回null
      • ** put() 往queue放进去一个element以后就一直wait直到有其他thread进来把这个element取走**
      • offer() 往queue里放一个element后立即返回,如果碰巧这个element被另一个thread取走了,offer方法返回true,认为offer成功;否则返回false.
      • offer(2000, TimeUnit.SECONDS) 往queue里放一个element但是等待指定的时间后才返回,返回的逻辑和offer()方法一样
      • ** take() 取出并且remove掉queue里的element(认为是在queue里的。。。),取不到东西他会一直等**
      • poll() 取出并且remove掉queue里的element(认为是在queue里的。。。),只有到碰巧另外一个线程正在往queue里offer数据或者put数据的时候,该方法才会取到东西。否则立即返回null
      • ** poll(2000, TimeUnit.SECONDS) 等待指定的时间然后取出并且remove掉queue里的element,其实就是再等其他的thread来往里塞**
      • isEmpty()永远是true
      • remainingCapacity() 永远是0
      • remove()和removeAll() 永远是false
    • 三:代码使用案列:
	 SynchronousQueue<Integer> queue = new SynchronousQueue<Integer>();
        Thread putThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("put thread start");
                try {
                    queue.put(1);
                } catch (InterruptedException e) {
                }
                System.out.println("put thread end");
            }
        });

        Thread takeThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("take thread start");
                try {
                    System.out.println("take from putThread: " + queue.take());
                } catch (InterruptedException e) {
                }
                System.out.println("take thread end");
            }
        });


        takeThread.start();
        Thread.sleep(1000);
        putThread.start();
        //控制台输出
		take thread start
		put thread start
		take from putThread: 1
		take thread end  
		put thread end  
  • 8、 BlockingQueue 代码案例( PriorityBlockingQueue用法)

    • 这是一个无界有序的阻塞队列,排序规则和之前介绍的PriorityQueue一致,只是增加了阻塞操作。同样的该队列不支持插入null元素,同时不支持插入非comparable的对象。它的迭代器并不保证队列保持任何特定的顺序,如果想要顺序遍历,考虑使用Arrays.sort(pq.toArray())。该类不保证同等优先级的元素顺序,如果你想要强制顺序,就需要考虑自定义顺序或者是Comparator使用第二个比较属性。
    • DEFAULT_INITIAL_CAPACITY:默认队列容量11
    • MAX_ARRAY_SIZE:最大可分配队列容量Integer.MAX_VALUE - 8,减8是因为有的VM实现在数组头有些内容
    • queue:队列元素数组。平衡二叉堆实现,父节点下标是n,左节点则是2n+1,右节点是2n+2。最小的元素在最前面
    • size:当前队列中元素的个数
    • comparator:决定队列中元素先后顺序的比较器
    • lock:所有public方法的锁
    • notEmpty:队列为空时的阻塞条件
    • allocationSpinLock:扩容数组分配资源时的自旋锁,CAS需要
    • q:PriorityQueue只用于序列化的时候,为了兼容之前的版本。只有在序列化和反序列化的时候不为null。
  • 9、BlockingQueue 代码案例( LinkedTransferQueue用法)

    • LinkedTransferQueue是 SynchronousQueue 和 LinkedBlockingQueue 的合体,性能比 LinkedBlockingQueue 更高(没有锁操作),比 SynchronousQueue能存储更多的元素。当 put 时,如果有等待的线程,就直接将元素 “交给” 等待者, 否则直接进入队列。
      put和 transfer 方法的区别是,put 是立即返回的, transfer 是阻塞等待消费者拿到数据才返回。transfer方法和 SynchronousQueue的 put 方法类似。
public interface TransferQueue<E> extends BlockingQueue<E> {
    // 如果可能,立即将元素转移给等待的消费者。 
    // 更确切地说,如果存在消费者已经等待接收它(在 take 或 timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则返回 false。
    boolean tryTransfer(E e);

    // 将元素转移给消费者,如果需要的话等待。 
    // 更准确地说,如果存在一个消费者已经等待接收它(在 take 或timed poll(long,TimeUnit)poll)中,则立即传送指定的元素,否则等待直到元素由消费者接收。
    void transfer(E e) throws InterruptedException;

    // 上面方法的基础上设置超时时间
    boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;

    // 如果至少有一位消费者在等待,则返回 true
    boolean hasWaitingConsumer();

    // 返回等待消费者人数的估计值
    int getWaitingConsumerCount();
}

ref: https://blog.csdn.net/wbwjx:
ref: https://www.cnblogs.com/gxyandwmm/p/9398934.html
ref: https://www.cnblogs.com/shamo89/p/7055039.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值