データ構造とアルゴリズム(VI) - キュー

まず、学習シラバス

  • キュー、分類、基本的な方法を定義します
  • キューの実装コード
  • 基礎となるキュー(配列、リンクリスト)
  • 分類キュー:キュー、循環キュー

第二に、キュー

  1. 定義されたキュー一高度、第アウトリストデータ構造線形制約の操作含まれ、エンキューをエンキュー()およびデキュー()二つの基本的な操作。

01queueベース

非常に基本的なデータ構造としては、アプリケーションキューは、非常に広いまた、キュー、同時キューをブロック循環キューなどの特定の追加機能、と特にいくつかのキューです。彼らは部分的に基礎となるシステム、フレーム、ミドルウェアの多くは、重要な役割を果たして開発しています。例えば、高性能キューかく乱、Linuxのリングバッファは、サイクル同時キューを使用していた。Javaの同時使用ArrayBlockingQueueとの契約は、公正なロックを達成するために、など

  1. キュー(配列)を達成するJava

// 用数组实现的队列
public class ArrayQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head表示队头下标,tail表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为capacity的数组
  public ArrayQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(String item) {
    // 如果tail == n 表示队列已经满了
    if (tail == n) return false;
    items[tail] = item;
    ++tail;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果head == tail 表示队列为空
    if (head == tail) return null;
    // 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了
    String ret = items[head];
    ++head;
    return ret;
  }
}

基本的なコード上記キューを満たす:アプリケーションは、一般的に、配列の大部分はヘッドは、より少ないテールポインタよりもできなくても、アドレスの後に充填されたときに、不足している記憶空間、エンキュー判定条件、デキュー条件判定に関しその後、チームに、そしてあなたが必要とする今回のデータ移動は、すべてのチームが操作を持つ新しい要素を挿入ではなく、後に、私たちはデータを移動する必要がある場合、キューの中の私たち単に空き領域のない、それが焦点に必要となった原因変換するエンキュー()関数のチームのニーズに、この時間を移動操作:


   // 入队操作,将item放入队尾
  public boolean enqueue(String item) {
    // tail == n表示队列末尾没有空间了
    if (tail == n) {
      // tail ==n && head==0,表示整个队列都占满了
      if (head == 0) return false;
      // 数据搬移
      for (int i = head; i < tail; ++i) {
        items[i-head] = items[i];
      }
      // 搬移完之后重新更新head和tail
      tail -= head;
      head = 0;
    }
    
    items[tail] = item;
    ++tail;
    return true;
  }

イラスト:

  1. ベースのリストキューの実装方法
  • ヘッドポインタとテールポインタ。彼らは、最初のノードと最後のリンクリストのノードを指します。示されているように、ときチーム、tail->次= new_node、尾= tail->次回、時間デキュー、ヘッド=頭部>次。

  1. 循環キュー
  • アレイベースの実装され、時にテール= n個のデータを移行する必要がある場合、損失がパフォーマンスが落ちる、それを回避するための方法はありませんか?これは、円形のキューです。
  • 04cycleQueue
  • 图中这个队列的大小为 8,当前 head=4,tail=7。当有一个新的元素 a 入队时,我们放入下标为 7 的位置。但这个时候,我们并不把 tail 更新为 8,而是将其在环中后移一位,到下标为 0 的位置。当再有一个元素 b 入队时,我们将 b 放入下标为 0 的位置,然后 tail 加 1 更新为 1。

完成循环队列的代码,最关键的是:确定好队空和队满的判定条件,队满的判断条件是 tail == n,队空的判断条件是 head == tail;

05cyclequeue

就像我图中画的队满的情况,tail=3,head=4,n=8,所以总结一下规律就是:(3+1)%8=4。多画几张队满的图,你就会发现,当队满时,(tail+1)%n=head;


public class CircularQueue {
  // 数组:items,数组大小:n
  private String[] items;
  private int n = 0;
  // head表示队头下标,tail表示队尾下标
  private int head = 0;
  private int tail = 0;

  // 申请一个大小为capacity的数组
  public CircularQueue(int capacity) {
    items = new String[capacity];
    n = capacity;
  }

  // 入队
  public boolean enqueue(String item) {
    // 队列满了
    if ((tail + 1) % n == head) return false;
    items[tail] = item;
    tail = (tail + 1) % n;
    return true;
  }

  // 出队
  public String dequeue() {
    // 如果head == tail 表示队列为空
    if (head == tail) return null;
    String ret = items[head];
    head = (head + 1) % n;
    return ret;
  }
}
  1. 阻塞队列与并发队列

阻塞队列其实就是在队列基础上增加了阻塞操作。简单来说,就是在队列为空的时候,从队头取数据会被阻塞。因为此时还没有数据可取,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。06queue

阻塞队列存在并发的问题:

07queue

线程安全的队列我们叫作并发队列。最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。实际上,基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因。在实战篇讲 Disruptor 的时候,我会再详细讲并发队列的应用。

三、引入问题:线程池没有空闲线程时,新的任务请求线程资源时,线程池该如何处理?各种处理策略又是如何实现的呢?

  • 我们一般有两种处理策略。第一种是非阻塞的处理方式,直接拒绝任务请求;另一种是阻塞的处理方式,将请求排队,等到有空闲线程时,取出排队的请求继续处理。那如何存储排队的请求呢?
  • このキュー・データ構造がキューに入れられた要求を格納するのに非常に適しているように、我々は、最初のサーブ、高度、各キューの要求の公平な扱いをしたいです。我々が言ったように、これらの2つの実装に基づいて、リンクされたリストや配列に基づいてキューがあります。キューに入れられた要求のための両方の実装は、それがどのような違いを生むのでしょうか?
  • リンクリストのベースの実装では、あなたは、アンバウンド形式のキュー(アンバウンド形式のキュー)のサポート無制限のラインを実現することができますが、長すぎる、あまりにも多くの要求がキューイングされている要求応答処理時間を生じ得ます。だから、無限のキュースレッドプールリンクリストの実装に基づいて、システムのより敏感な応答時間のために適切ではありません。
  • 有界(有界キュー)上のキュー、キューを達成するために、配列の有限のサイズ、スレッドプールが要求をキューに入れられたが、キューのサイズを超えて、次の要求は、このような応答時間に敏感なシステムでは、拒否されます、比較的より合理的。しかし、キューサイズの合理的なセットは、非常に豪華です。キューは、あまりにも多くの保留中の要求に大きすぎると、キューが小さすぎると、システムリソース、最大化の性能をフルに活用をしないためにつながります。
  • キュー外スレッドプールシーンキューに入れられた前述のアプリケーション・キュー・リクエストに加えて、そのようなデータベース接続プールなど、要求をキューイングするための任意の限られたリソースプールに適用することができます。空きリソースがない場合に実際には、ほとんどの限られた資源のシナリオのために、要求キュー「キュー」は、このデータ構造によって、基本的に実装することができます。

第四に、放課後の思考

  1. このプールの構造は、要求をキューイングに使用されるスレッドプールに加えて、あなたはまた、同様のセル構造やシーンはキューがそれを要求キューイングに使用されるかを知っていますか?
  2. 今日はロックフリー同時キューを達成するためにどのように同時キューについて話しました、オンラインディスカッションがたくさんあります。この問題では、どのようにそれを見ていますか?

おすすめ

転載: www.cnblogs.com/liweiweicode/p/11966228.html