如何用訊號量去實現一個限流器?

1)什麼是訊號量呢?

  • 65年斯特拉提出來訊號量,一直用到80年左右都是用它來解決並發的問題,直到管程模型出來了,他就逐漸落魄了。訊號量你可以類比生活中的紅綠燈,綠燈行紅燈停。

2)訊號量模型是怎樣的?

  • 一個計數器,一個等待隊列,3個方法(init(),up(),dowm())

     

     

3)訊號量的工作原理是什麼?

  • 當執行緒進臨界資源的時候,先把count-1,然後看是不是<0的,如果<0那麼對不起,不能進入 ,老老實實進等待隊列去。如果>=0,那麼可以進入,出來的時候把count+1,當count>=0了,那麼就去喚醒等待隊列的一個執行緒。

4)有了管程了,那訊號量還有存在的意義嗎?

  • 有,訊號量他有自己的不可替代性,他允許多個執行緒進入臨界資源,這個對我們的池化技術實現是有很大幫助的,比如我們的資料庫連接池,對象池。

5)如何用訊號量快速實現一個限流器?

  • 比如現在我們的需求是10個執行緒為上限,超過10個就限制進入。那我可以這樣設計,count初始化為10,那麼我10個執行緒拿到count的時候,都是>=0的,第11個執行緒來的時候,把count-1,count=-1小於0了,所以進等待隊列去。

     
     class ObjPool<T, R> {
       final List<T> pool;
       // 用訊號量實現限流器
       final Semaphore sem;
       // 構造函數
       ObjPool(int size, T t){
         pool = new Vector<T>(){};
         for(int i=0; i<size; i++){
           pool.add(t);
        }
         sem = new Semaphore(size);
      }
       // 利用對象池的對象,調用func
       R exec(Function<T,R> func) {
         T t = null;
         sem.acquire();
         try {
           t = pool.remove(0);
           return func.apply(t);
        } finally {
           pool.add(t);
           sem.release();
        }
      }
     }
     // 創建對象池
     ObjPool<Long, String> pool =
       new ObjPool<Long, String>(10, 2);
     // 通過對象池獲取t,之後執行  
     pool.exec(t -> {
         System.out.println(t);
         return t.toString();
     });

6)思考一下,上面的限流器使用的是vector來做對象集合的,那可不可以用ArrayList呢?

  • 不可以的,ArrayList是執行緒不安全的,而我們上面的對象池他是允許多個執行緒進入來訪問的,不用vector的話容易引發執行緒故障。造成並發執行了。