天天看點

Android 多線程之阻塞隊列

Android 多線程系列

阻塞隊列

  • 阻塞隊列常用于生産者和消費者的場景,生産者是往隊列裡添加元素的線程,消費者是從隊列裡取元素的線程。阻塞隊列就是生産者存放元素的容器,而消費者也隻從容器中取元素
  • 阻塞場景
    • 當隊列中沒有資料的情況下,消費者端的所有線程都會被自動阻塞,直到有資料放入隊列
    • 當隊列中填滿資料的情況下,生産者端的所有線程都會被自動阻塞,知道隊列中有空的位置
  • Java中提供了7種阻塞隊列,分别是ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue、LinkedTransferQueue、LinkedBlockingDeque,它們都實作了BlockingQueue接口

BlockingQueue接口

  • BlockingQueue接口提供了一些阻塞隊列的通用方法,如offer,poll方法等,下面簡單介紹幾個方法
  • offer(E var1):表示将var1添加到BlockingQueue,如果添加成功傳回true,否則傳回false。本方法不阻塞目前執行方法的線程
  • offer(E var1, long var2, TimeUnit var4):可以設定等待的時間,如果在指定的時間内還不能往隊列裡添加,則傳回失敗
  • put(E var1):将var1添加到BlockingQueue,如果BlockingQueue沒有空間了,那調用此方法的線程會被阻塞,直到BlockingQueue裡面有空間再繼續添加
  • poll(long var1, TimeUnit var3):從BlockingQueue首位取出資料,如果在指定的時間内,隊列一旦有資料可取,就立即傳回隊列中的資料,否則逾時傳回null
  • take(): 取走BlockingQueue裡的排在首位的元素,如果BlockingQueue為空,則阻塞進入等待狀态,直到BlockingQueue有新的資料加入
  • drainTo:一次性從BlockingQueue擷取所有可用的對象,還可以指定擷取資料的個數。通過該方法可以提升擷取資料的效率,無須多次分批加鎖或釋放鎖

阻塞隊列的實作原理

ArrayBlockingQueue
  • ArrayBlockingQueue源碼解析
  • ArrayBlockingQueue是一個用數組實作的有界阻塞隊列,通過全局獨占鎖來實作出隊和入隊操作,同時隻能有一個線程進行入隊或出隊操作
  • ArrayBlockingQueue的offer、poll通過簡單的加鎖進行入隊出隊操作,并且不會阻塞線程;而put、take則通過重入鎖的條件對象實作隊列滿則等待、隊列空則等待,會阻塞目前線程
  • ArrayBlockingQueue能通過size方法擷取準确的隊列元素個數
LinkedBlockingQueue
  • LinkedBlockingQueue源碼解析
  • LinkedBlockingQueue是一個基于連結清單的隊列,并且是一個先進先出的隊列。
  • LinkedBlockingQueue如果不指定隊列的容量,預設容量大小為Integer.MAX_VALUE,有可能造成占用記憶體過大的情況
  • LinkedBlockingQueue内部對入隊和出隊操作采用了不同的鎖,這樣入隊和出隊操作可以并發進行。但同時隻能有一個線程可以進行入隊或出隊操作。
  • LinkedBlockingQueue内部采用的是可重入獨占的非公平鎖,并且通過重入鎖的條件變量來進行出隊和入隊的同步
  • LinkedBlockingQueue通過操作原子變量count來擷取目前隊列的元素個數
SynchronousQueue
  • SynchronousQueue源碼解析
  • SynchronousQueue本身沒有容量存儲元素,但是它是通過管理送出操作的線程隊列來實作阻塞隊列的
  • SynchronousQueue可以實作控制線程先進先出進行排序,也就是先被挂起的線程先被喚醒,這個内部是通過連結清單來實作的。SynchronousQueue預設是不保證證喚醒的順序的
  • SynchronousQueue的不帶逾時時間的offer和poll方法不會挂起線程,而take和put方法可能會挂起線程。
  • SynchronousQueue一個典型的應用場景是線程池newCachedThreadPool,如果入隊操作和出隊操作的處理速度相差比較大的話有可能會建立大量線程,有耗盡記憶體的風險
DelayQueue
  • DelayQueue源碼分析
  • DelayQueue是基于優先級PriorityQueue實作的,而PriorityQueue的預設構造方法設定容量為11,是以DelayQueue是有界的
  • DelayQueue中的元素都必須實作Delayed接口的getDelay方法,以便可以定時執行任務
  • DelayQueue中的元素不一定會按照添加的順序,而是根據元素的優先級排序,元素可以通過實作Comparable接口來定制排列的順序
  • DelayQueue的add、put、offer和poll方法不會挂起線程,而take和帶有逾時時間的poll方法可能會挂起目前線程
  • DelayQueue通過全局獨占鎖來實作同步,這意味着同時隻能有一個入隊或是出隊操作

歡迎關注我的微信公衆号,期待與你一起學習,一起交流,一起成長!

繼續閱讀