java多線程與高并發(fā)(七)并發(fā)容器

1.并發(fā)容器

java容器

1.1.第一類Collection螟凭,也叫做集合

集合的意思就是說(shuō)這個(gè)容器是什么結(jié)構(gòu)板熊,你都可以把一個(gè)元素一個(gè)元素的往里面添加帘饶。從數(shù)據(jù)結(jié)構(gòu)的角度來(lái)說(shuō)甜无,這個(gè)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu)也就兩種
連續(xù)存儲(chǔ)的數(shù)組Array,和非連續(xù)存儲(chǔ)的一個(gè)指向另外一個(gè)的鏈表喳张,但是邏輯結(jié)構(gòu)就很多了续镇。Collection又分成了3大類,分別為Set,List,Queue蹲姐,Queue隊(duì)列接口就是為了高并發(fā)準(zhǔn)備的磨取,Set不會(huì)有重復(fù)的元素人柿。隊(duì)列最主要的原因就是實(shí)現(xiàn)了任務(wù)的狀態(tài)和獲取,叫做阻塞隊(duì)列忙厌,其中有一個(gè)子接口Deque叫做雙端隊(duì)列凫岖,一般的隊(duì)列只能一端進(jìn)另外一端出,但是雙端隊(duì)列卻可以同進(jìn)同出逢净。

1.2.第二類Map哥放,集合特殊的變種,一對(duì)一對(duì)的

1.2.1.HashTable

最開始java1.0的容器里只有兩個(gè)爹土,Vector和Hashtable,但是這兩個(gè)容器所有的默認(rèn)方法都是加了鎖synchronized的甥雕,這是最早設(shè)計(jì)不太合理的地方。后來(lái)又加了HashMap,它是完全沒(méi)有加鎖的胀茵,后來(lái)為了適配有鎖的場(chǎng)景又加了SynchronizedMap社露,它是HashMap的加鎖版本。
Vector和Hashtable 自帶鎖琼娘,基本不用

1.2.2.HashMap

我們來(lái)看看這HashMap峭弟,同學(xué)們想一下這個(gè)HashMap往里頭插會(huì)不會(huì)有問(wèn)題,因?yàn)镠ashMap沒(méi)有鎖脱拼,線程不安全瞒瘸。雖然他速度比較快,但是數(shù)據(jù)會(huì)出問(wèn)題熄浓,并且可能報(bào)各種異常情臭。

主要是因?yàn)樗鼉?nèi)部會(huì)把這個(gè)變成TreeNode。具體細(xì)節(jié)不再細(xì)究

package com.learn.thread.six;

import com.learn.thread.six.enums.Constans;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.UUID;

public class TestHashMap {

    private static HashMap<UUID, UUID> m = new HashMap<>();

    static int count = Constans.COUNT;

    static UUID[] keys = new UUID[count];

    static UUID[] values = new UUID[count];

    static final int THREAD_COUNT = Constans.THREAD_COUNT;

    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    static class Mythread extends Thread {

        /**
         * 開始位置
         */
        int start;

        /**
         * 每一個(gè)線程放入的個(gè)數(shù)
         */
        int gap = count/THREAD_COUNT;

        public Mythread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            for (int i = start; i < start + gap; i++) {
                m.put(keys[i], values[i]);
            }
        }

        public static void main(String[] args) {
            long start = System.currentTimeMillis();

            Thread[] threads = new Thread[THREAD_COUNT];

            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Mythread(i * (count/THREAD_COUNT));
            }
            for (Thread item : threads) {
                item.start();
            }

            for (Thread thread : threads) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();

            System.out.println(end - start);

            System.out.println(m.size());
        }
    }
}

可以看到赌蔑,HashMap不是線程安全的

1.2.3.SynchronizedHashMap

這個(gè)是給HashMap手動(dòng)加了鎖俯在,它的源碼自己做了一個(gè)Object,然后每次都是SynchronizedObject娃惯,嚴(yán)格來(lái)講他和那個(gè)HashMap效率上區(qū)別不大

package com.learn.thread.six;

import com.learn.thread.six.enums.Constans;

import java.util.*;

public class TestSynchronziedHashMap {

  private static Map<UUID, UUID> m = Collections.synchronizedMap(new HashMap<UUID, UUID>());

  static int count = Constans.COUNT;

  static UUID[] keys = new UUID[count];

  static UUID[] values = new UUID[count];

  static final int THREAD_COUNT = Constans.THREAD_COUNT;

  static {
      for (int i = 0; i < count; i++) {
          keys[i] = UUID.randomUUID();
          values[i] = UUID.randomUUID();
      }
  }

  static class Mythread extends Thread {

      /**
       * 開始位置
       */
      int start;

      /**
       * 每一個(gè)線程放入的個(gè)數(shù)
       */
      int gap = count/THREAD_COUNT;

      public Mythread(int start) {
          this.start = start;
      }

      @Override
      public void run() {
          for (int i = start; i < start + gap; i++) {
              m.put(keys[i], values[i]);
          }
      }

      public static void main(String[] args) {
          long start = System.currentTimeMillis();

          Thread[] threads = new Thread[THREAD_COUNT];

          for (int i = 0; i < threads.length; i++) {
              threads[i] = new Mythread(i * (count/THREAD_COUNT));
          }
          for (Thread item : threads) {
              item.start();
          }

          for (Thread thread : threads) {
              try {
                  thread.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          long end = System.currentTimeMillis();

          System.out.println(end - start);

          System.out.println(m.size());

          // ---------------------------------
          start = System.currentTimeMillis();

          for (int i = 0; i < threads.length; i++) {
              threads[i] = new Thread(() -> {
                  for (int j = 0; j < 10; j++) {
                      System.out.println(m.get(keys[10]));
                  }
              });
          }

          for (Thread item : threads) {
              item.start();
          }
          for (Thread item : threads) {
              try {
                  item.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
          end = System.currentTimeMillis();
          System.out.println(end - start);
      }
  }
}

1.2.4.ConcurrentHashMap

ConcurrentHashMap 是多線程里邊真正用得到的朝巫,這個(gè)效率主要體現(xiàn)在讀上面,由于它往里邊插的時(shí)候做了各種判斷石景,本來(lái)是鏈表的,后來(lái)改成了紅黑樹拙吉,然后又做了各種各樣的CAS判斷潮孽,所以插入的時(shí)候是更低一點(diǎn)。
HashMap和HashTable層雖然讀的效率稍微低一點(diǎn)筷黔,但是他往里插的時(shí)候檢查的東西比較少往史,就加個(gè)鎖往后一插。
所以佛舱,各種Map的使用椎例,看你實(shí)際當(dāng)中的需求挨决。下面用個(gè)程序看看ConcurrentHashMap的查詢速度和插入速度

package com.learn.thread.six;

import com.learn.thread.six.enums.Constans;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class TestConcurrentHashMap {

    private static Map<UUID, UUID> m = new ConcurrentHashMap<>();

    static int count = Constans.COUNT;

    static UUID[] keys = new UUID[count];

    static UUID[] values = new UUID[count];

    static final int THREAD_COUNT = Constans.THREAD_COUNT;

    static {
        for (int i = 0; i < count; i++) {
            keys[i] = UUID.randomUUID();
            values[i] = UUID.randomUUID();
        }
    }

    static class Mythread extends Thread {

        /**
         * 開始位置
         */
        int start;

        /**
         * 每一個(gè)線程放入的個(gè)數(shù)
         */
        int gap = count/THREAD_COUNT;

        public Mythread(int start) {
            this.start = start;
        }

        @Override
        public void run() {
            for (int i = start; i < start + gap; i++) {
                m.put(keys[i], values[i]);
            }
        }

        public static void main(String[] args) {
            long start = System.currentTimeMillis();

            Thread[] threads = new Thread[THREAD_COUNT];

            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Mythread(i * (count/THREAD_COUNT));
            }
            for (Thread item : threads) {
                item.start();
            }

            for (Thread thread : threads) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();

            System.out.println(end - start);

            System.out.println(m.size());

            // ---------------------------------
            start = System.currentTimeMillis();

            for (int i = 0; i < threads.length; i++) {
                threads[i] = new Thread(() -> {
                    for (int j = 0; j < 10; j++) {
                        System.out.println(m.get(keys[10]));
                    }
                });
            }

            for (Thread item : threads) {
                item.start();
            }
            for (Thread item : threads) {
                try {
                    item.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }
}

1.2.5.ConcurrentSkipListMap

通過(guò)跳表來(lái)實(shí)現(xiàn)的高并發(fā)容器,并且這個(gè)Map是有排序的
跳表: 底層本身存儲(chǔ)的是一個(gè)鏈表元素订歪,是排好序的脖祈。如果鏈表是排好序的,往里邊插入和取值都變得特別麻煩刷晋。因?yàn)槟愕脧念^到尾的找盖高。這時(shí)候跳表就出現(xiàn)了
跳表在鏈表的基礎(chǔ)上拿出了一些關(guān)鍵元素,在上面做一層鏈表眼虱,如果這一層數(shù)據(jù)量還是很大喻奥,就在這個(gè)基礎(chǔ)上再抽一些元素做一個(gè)鏈表。所以查找數(shù)據(jù)的時(shí)候是一層一層的往下查找捏悬,實(shí)現(xiàn)上比TreeMap 又容易了很多撞蚕。

下面做一個(gè)小程序,試試這幾個(gè)Map 的性能

package com.learn.thread.six.map;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CountDownLatch;

public class TestConcurrentSkipListMap {
    public static void main(String[] args) {
        Map<String, String> map1 = new ConcurrentSkipListMap<>();
        Map<String, String> map2 = new ConcurrentHashMap<>();
        Map<String, String> map3 = new HashMap<>();
        Hashtable<String, String> map4 = new Hashtable<>();
        test(map1);
        test(map2);
        test(map3);
        test(map4);
    }
    public static void test(Map<String, String> map) {
        Random random = new Random();
        Thread[] tds = new Thread[100];
        CountDownLatch count = new CountDownLatch(tds.length);
        long start = System.currentTimeMillis();
        for (int i = 0; i < tds.length; i++) {
            tds[i] = new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    map.put(UUID.randomUUID().toString(), "a" + random.nextInt(100000));
                }
                count.countDown();
            });
        }
        Arrays.asList(tds).forEach(item -> item.start());
        try {
            count.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
        System.out.println(map.size());


    }
}

1.2.6.ArraryList

我們先來(lái)認(rèn)識(shí)一下vector到Queue的發(fā)展歷程过牙,下面有一個(gè)程序展示超賣的現(xiàn)象

package com.learn.thread.six.list;

import java.util.ArrayList;
import java.util.List;

public class TestTicketSeller {

    private static List<String> tickets = new ArrayList<>();

    static {
        for (int i = 0; i < 1000; i++) {
            tickets.add("票編號(hào)" + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (tickets.size() >0 ){
                    System.out.println("銷售了 ---" + tickets.remove(0));
                }
            }).start();
        }
    }
}

運(yùn)行一下甥厦,我們發(fā)現(xiàn)賣了超過(guò)1000條

1.2.7.Vector

我們來(lái)看看最早的容器Vector,內(nèi)部是帶鎖的,(add 和remove 都是帶鎖的)抒和,但是我們的代碼邏輯中間Vector 是不會(huì)帶鎖的矫渔。

package com.learn.thread.six.list;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

public class TestSeller2 {
    private static Vector<String> tickets = new Vector<>();

    static {
        for (int i = 0; i < 100; i++) {
            tickets.add("票編號(hào)" + i);
        }
    }
    public static void main(String[] args) {
        testSeller();
    }

    public static void testSeller() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (tickets.size() >0 ){
                    // 下面這段代碼并沒(méi)有加鎖
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("銷售了 ---" + tickets.remove(0));
                }
            }).start();
        }
    }
}

可以看到,中間代碼睡眠2秒是存在線程安全問(wèn)題的摧莽,如果把它去掉庙洼,整個(gè)程序不會(huì)報(bào)異常。所以還是得最外層加一個(gè)鎖镊辕。如下所示

public static void testSynchronizedSeller() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                // 保證原子性操作
                synchronized (tickets) {
                    while (tickets.size() >0 ){
                        // 下面這段代碼并沒(méi)有加鎖
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("銷售了 ---" + tickets.remove(0));
                    } 
                }
            }).start();
        }
    }

1.2.8.LinkedList

LinkList是一個(gè)雙鏈表,在添加和刪除元素時(shí)具有比ArrayList更好的性能.但在get與set方面弱于ArrayList.當(dāng)然,這些對(duì)比都是指數(shù)據(jù)量很大或者操作很頻繁油够。

LinkedList并不需要連續(xù)的空間,大小不確定


package com.learn.thread.six.list;

import java.util.Vector;

public class TestLinkedList {
    private static Vector<String> tickets = new Vector<>();

    static {
        for (int i = 0; i < 10; i++) {
            tickets.add("票編號(hào)" + i);
        }
    }

    public static void main(String[] args) {
        testSeller();
    }

    public static void testSeller() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    synchronized (tickets) {
                        if (tickets.size() <= 0) {
                            return;
                        }

                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("銷售了------" + tickets.remove(0));
                    }
                }
            }).start();
        }
    }
}

1.2.9.Queue

效率最高的就是這個(gè)Queue,這是Java 最新的一個(gè)借口征懈,主要目標(biāo)就是為了高并發(fā)使用的石咬,就是為了多線程,所以以后多線程就考慮Queue卖哎。
Queue 使用最多的就是ConcurrentLinkedQueue,然后里邊沒(méi)有加鎖鬼悠,調(diào)用一個(gè)叫poll的方法去取值,如果取空了就說(shuō)明里面的值已經(jīng)沒(méi)了亏娜。

package com.learn.thread.six.queue;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;

public class TestQueue {
    static Queue<String> queue = new ConcurrentLinkedDeque<>();

    static {
        for (int i = 0; i < 10; i++) {
            queue.add("票編號(hào)" + i);
        }
    }

    public static void main(String[] args) {
        testQueue();
    }
    public static void testQueue() {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                while (true) {
                    String s = queue.poll();
                    if (s == null) {
                        return;
                    }
                    System.out.println("銷售了" + s);
                }
            }).start();
        }
    }
}

1.2.10.CopyOnWrite

再來(lái)看看并發(fā)的時(shí)候經(jīng)常使用的一個(gè)類焕窝,CopyOnWrite ,CopyOnWirteList维贺,CopyOnWirteSet, CopyOnwirte的意思就是復(fù)制,并且是寫時(shí)復(fù)制

public boolean add(E e) {
    // 添加的時(shí)候它掂,上鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 原本的數(shù)組
        Object[] elements = getArray();
        // 原本數(shù)組的長(zhǎng)度
        int len = elements.length;
        // 調(diào)用native方法進(jìn)行復(fù)制
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        // 新的元素
        newElements[len] = e;
        // 替換數(shù)組
        setArray(newElements);
        // 成功
        return true;
    } finally {
        // 解鎖
        lock.unlock();
    }
}

package com.learn.thread.six.queue;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

public class TestCopyOnwirte {
    public static void main(String[] args) {
        List<Integer> list1 = new CopyOnWriteArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<Integer> list3 = new Vector<>();
        List<Integer> list4 = new LinkedList<>();
        test(list1);
        test(list2);
        test(list3);
        test(list4);
    }

    public static void test(List<Integer> list) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 100; j++) {
                int finalJ = j;
                new Thread(() -> {
                    list.add(finalJ);
                }) .start();
            }
        }
        System.out.println(list.size());
        long end = System.currentTimeMillis();
        System.out.println("time" + (end - start));
    }
}

可以發(fā)現(xiàn)CopyOnWirte的寫是最慢的,所以這個(gè)適用于溯泣,多讀少寫的場(chǎng)景虐秋。如果對(duì)象很大榕茧,CopyOnWirte存在內(nèi)存溢出的情況,因?yàn)閷懖僮鞫际菑?fù)制一份客给,需要開辟另外一個(gè)空間

3.隊(duì)列

3.1.BlockingQueue

這個(gè)是后面線程池的時(shí)候需要講的內(nèi)存用押,重點(diǎn)就是Block阻塞,顧名思義起愈,阻塞隊(duì)列只恨,我們可以在其方法上做到線程自動(dòng)阻塞。
offer offer是往里頭添加抬虽,加沒(méi)加進(jìn)去會(huì)返回一個(gè)boolean值
add add 同樣往里頭加官觅,但是如果沒(méi)加進(jìn)去是會(huì)拋出異常的
poll 提取數(shù)據(jù),并且remove掉
peek 提取數(shù)據(jù)阐污,但是不remove

package com.learn.thread.six.queue;

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;

public class TestBlockingQueue {

    public static void main(String[] args) {
        Queue<String> queue = new ConcurrentLinkedDeque<>();
        for (int i = 0; i < 10; i++) {
            queue.offer("a" + i);
        }
        System.out.println(queue);
        System.out.println(queue.size());
        // 隊(duì)列先進(jìn)后出休涤,poll是提取最后一個(gè)進(jìn)去的
        System.out.println(queue.poll());
        System.out.println(queue.peek());
        System.out.println(queue.size());
    }
}

3.2.LinkedBlockingQueue

LinkedBlockingQueue 是用鏈表實(shí)現(xiàn)的無(wú)界隊(duì)列,一直往里頭加笛辟,直到內(nèi)存裝滿了為止
LinkedBlockingQueue 在Queue的基礎(chǔ)上功氨,添加兩個(gè)很重要的方法
put 往隊(duì)列里邊加,加滿了手幢,這個(gè)線程就會(huì)阻塞住捷凄。
take 往外取數(shù)據(jù),如果空了围来,線程也會(huì)阻塞住
所以LinkedBlockingQueue是實(shí)現(xiàn)生產(chǎn)者消費(fèi)者最好的容器跺涤。
下面以一個(gè)小程序演示一下一個(gè)線程a到i 裝數(shù)據(jù),沒(méi)裝一個(gè)睡一秒监透,另外啟5個(gè)線程不斷的從里面take桶错,空了我就等著,什么時(shí)候新加了我就立馬把他取出來(lái)

package com.learn.thread.six.queue;


import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class TestLinkedBlockingQueue {
    static BlockingQueue<String> queue = new LinkedBlockingQueue<>(3);

    static Random random = new Random();

    public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                // 如果滿了就會(huì)等待
                try {
                    queue.put("a" + i);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        },"p1").start();

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                while (true) {
                    try {
                        System.out.println(Thread.currentThread().getName() + queue.take());
                    } catch (Exception ex) {
                    }
                }
            }).start();
        }
    }
}

3.3.ArrayBlockingQueue

ArrayBlockingQueue 是有界的胀蛮,你可以指定一個(gè)固定的值院刁,他的容器就是10,那么當(dāng)你往里面扔內(nèi)容的時(shí)候粪狼,一旦滿了put方法就是阻塞住退腥,如果繼續(xù)add 的話就會(huì)拋出異常.
offer 用返回值來(lái)判斷是否添加成功,還有另外一個(gè)寫法再榄,你可以指定一個(gè)時(shí)間嘗試往里邊添加阅虫,比如一秒鐘,如果一秒鐘內(nèi)沒(méi)有添加成功不跟,他就返回了。
面試題經(jīng)常會(huì)被問(wèn)到 Queue和List 的區(qū)別是什么
答:添加了offer\peek\poll\put\take 這些方法對(duì)線程友好的阻塞或者等待米碰。

package com.learn.thread.six.queue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class TestArrayLBlockingQueue {
    public static BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            queue.put("a" + i);
        }
        // 滿了就會(huì)阻塞
        queue.put("a");
//        queue.add("a");
        queue.offer("a");
        queue.offer("a", 1, TimeUnit.SECONDS);
        System.out.println(queue);
    }
}

下面看看幾個(gè)比較特殊的Queue窝革,這些都是BlockingQueue 购城,同樣也是阻塞的

3.4.DelayQueue

DelayQueue 可以實(shí)現(xiàn)時(shí)間上的排序,這個(gè)DelayQueue能實(shí)現(xiàn)按里面等待的時(shí)間來(lái)排序虐译。
但是要實(shí)現(xiàn)Comparable接口重寫compareTo來(lái)確定是怎么排序的瘪板,DelayQueue就是按時(shí)間順序進(jìn)行任務(wù)調(diào)度

package com.learn.thread.six.queue;

import lombok.ToString;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class TestDelayQueue {
    private static BlockingQueue queue = new DelayQueue<>();

    @ToString
    static class MyThread implements Delayed {
        String name;

        long runningTime;

        public MyThread(String name, long runningTime) {
            this.name = name;
            this.runningTime = runningTime;
        }

        /**
         * 根據(jù)這個(gè)時(shí)間去排序
         *
         * @param unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        }

        // 修改排序
        @Override
        public int compareTo(Delayed o) {
            if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {
                return -1;
            } else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
                return 1;
            } else {
                return 0;
            }
        }

        public static void main(String[] args) throws InterruptedException {
            long now = System.currentTimeMillis();
            MyThread t1 = new MyThread("t1", now + 1000);
            MyThread t2 = new MyThread("t2", now + 2000);
            MyThread t3 = new MyThread("t3", now + 1500);
            MyThread t4 = new MyThread("t4", now + 2500);
            MyThread t5 = new MyThread("t5", now + 500);
            queue.put(t1);
            queue.put(t2);
            queue.put(t3);
            queue.put(t4);
            queue.put(t5);
            System.out.println(queue);
            for (int i = 0; i < 5; i++) {
                System.out.println(queue.take());
            }
        }
    }
}

DelayQueue本質(zhì)上是用了個(gè)PriorityQueue,PriorityQueue是AbstractQueue繼承的。PriorityQueue的特點(diǎn)是它內(nèi)部往里裝的時(shí)候不是按順序的漆诽,而是內(nèi)部進(jìn)行了一個(gè)排序侮攀。按照優(yōu)先級(jí),最小的優(yōu)先厢拭,內(nèi)部結(jié)構(gòu)是一個(gè)二叉樹兰英,這個(gè)二叉樹可以認(rèn)為是堆排序里面那個(gè)最小堆值排在最上面。

package com.learn.thread.six.queue;

import java.util.PriorityQueue;

public class TestPriorityQueue {
    public static void main(String[] args) {
        PriorityQueue<String> queue = new PriorityQueue<>();

        queue.add("a");
        queue.add("e");
        queue.add("b");
        queue.add("c");
        queue.add("f");
        for (int i = 0; i < 5; i++) {
            System.out.println(queue.poll());
        }
    }
}

3.5.SynchronousQueue

SynchronousQueue的容量為0供鸠,這個(gè)容器不是用來(lái)裝內(nèi)容的畦贸,專門用來(lái)兩個(gè)線程之間傳內(nèi)容的,給線程下達(dá)任務(wù)的楞捂,跟之前講過(guò)的Exchange一樣薄坏。
add方法會(huì)直接報(bào)錯(cuò),原因是容量為0寨闹,不能添加胶坠。只能用put調(diào)用,并且是要求前面有人拿著這個(gè)東西你才可以往里面裝
SynchronousQueue線程池多數(shù)情況下會(huì)使用繁堡,很多線程取任務(wù)沈善,互相之間進(jìn)行任務(wù)調(diào)度的都是他

package com.learn.thread.six.queue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;

public class TestSynchronousQueue {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new SynchronousQueue<>();
        new Thread(() -> {
            try {
                System.out.println(queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        // 阻塞等待消費(fèi)者消費(fèi),如果沒(méi)有被消費(fèi)會(huì)一直等待
        queue.put("aaa");
        System.out.println("niadsasdasd");
        System.out.println(queue.size());
    }
}

3.6.TransferQueue

Transfer意思為傳遞帖蔓,實(shí)際上和SynchronousQueue一樣矮瘟,不過(guò)SynchronousQueue只能傳遞一個(gè),它能傳遞多個(gè)塑娇,并且它還有一個(gè)transfer方法澈侠,意思就是裝完后就在這里等著被消費(fèi)。
一般使用場(chǎng)景埋酬,我做了一件事情哨啃,這個(gè)事情一定要有一個(gè)結(jié)果,有了結(jié)果之后我才可以繼續(xù)進(jìn)行我下面的事情写妥。
比如說(shuō)我支付了錢拳球,這個(gè)訂單付款完了,也是要一直等這個(gè)付賬結(jié)果完了了我才可以給客戶反饋

package com.learn.thread.six.queue;

import java.util.concurrent.LinkedTransferQueue;

import static java.lang.Thread.sleep;

public class TestTransferQueue {
    public static void main(String[] args) throws InterruptedException {
        LinkedTransferQueue<String> queue = new LinkedTransferQueue<String>();
        new Thread(() -> {
            try {
                System.out.println(queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        queue.transfer("asdasdasd");
        new Thread(() -> {
            try {
                System.out.println(queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

容器這章主要為了后面的線程池

總結(jié)
從hashTable到ConcurrentHashMap珍特,這不是一個(gè)替代關(guān)系祝峻,而是不同場(chǎng)景下不同的使用
Vector 到Queue這樣一個(gè)過(guò)程,主要是Vector添加了許多對(duì)線程友好的API,offer\peek\poll莱找,它的一個(gè)子類叫BlockingQueue又添加了put和take 實(shí)現(xiàn)了阻塞操作酬姆。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奥溺,隨后出現(xiàn)的幾起案子辞色,更是在濱河造成了極大的恐慌,老刑警劉巖浮定,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件相满,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡桦卒,警方通過(guò)查閱死者的電腦和手機(jī)立美,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)闸盔,“玉大人悯辙,你說(shuō)我怎么就攤上這事∮常” “怎么了躲撰?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)击费。 經(jīng)常有香客問(wèn)我拢蛋,道長(zhǎng),這世上最難降的妖魔是什么蔫巩? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任谆棱,我火速辦了婚禮,結(jié)果婚禮上圆仔,老公的妹妹穿的比我還像新娘垃瞧。我一直安慰自己,他們只是感情好坪郭,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布个从。 她就那樣靜靜地躺著,像睡著了一般歪沃。 火紅的嫁衣襯著肌膚如雪嗦锐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天沪曙,我揣著相機(jī)與錄音奕污,去河邊找鬼。 笑死液走,一個(gè)胖子當(dāng)著我的面吹牛碳默,可吹牛的內(nèi)容都是我干的贾陷。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼嘱根,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昵宇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起儿子,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砸喻,沒(méi)想到半個(gè)月后柔逼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡割岛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年愉适,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癣漆。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡维咸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惠爽,到底是詐尸還是另有隱情癌蓖,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布婚肆,位于F島的核電站租副,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏较性。R本人自食惡果不足惜用僧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赞咙。 院中可真熱鬧责循,春花似錦、人聲如沸攀操。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崔赌。三九已至意蛀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間健芭,已是汗流浹背县钥。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慈迈,地道東北人若贮。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓省有,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谴麦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蠢沿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容