1. 程式人生 > >java筆記--對訊號量Semaphore的理解與運用

java筆記--對訊號量Semaphore的理解與運用

https://www.cnblogs.com/XHJT/p/3910406.html

java Semaphore 訊號量的使用:

 

在java中,提供了訊號量Semaphore的支援。


Semaphore類是一個計數訊號量,必須由獲取它的執行緒釋放, 
通常用於限制可以訪問某些資源(物理或邏輯的)執行緒數目。


一個訊號量有且僅有3種操作,且它們全部是原子的:初始化增加減少

增加可以為一個程序解除阻塞; 
減少可以讓一個程序進入阻塞。

訊號量維護一個許可集,若有必要,會在獲得許可之前阻塞每一個執行緒: 
          //從此訊號量獲取給定數目的許可,在提供這些許可前一直將執行緒阻塞。 
          acquireUninterruptibly(int permits){}

 
每一個release()新增一個許可,從而可能釋放一個正在阻塞的獲取者。 
Semaphore只對可用許可的號碼進行計數,並採取相應的行動。 
  
如何獲得Semaphore物件? 
    public Semaphore(int permits,boolean fair) 
    permits:初始化可用的許可數目。 
    fair: 若該訊號量保證在徵用時按FIFO的順序授予許可,則為true,否則為false; 
    
如何從訊號量獲得許可? 

    public void acquire() throws InterruptedException

 

如何釋放一個許可,並返回訊號量? 
    public void release() 
    
程式碼例項: 
    20個人去銀行存款,但是該銀行只有兩個辦公櫃檯,有空位則上去存錢,沒有空位則只能去排隊等待。

package com.xhj.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
 * 執行緒訊號量Semaphore的運用
 * 
 * @author XIEHEJUN
 * 
 */
public class SemaphoreThread {
    private int a = 0;

    /**
     * 銀行存錢類
     */
    class Bank {
        private int account = 100;

        public int getAccount() {
            return account;
        }

        public void save(int money) {
            account += money;
        }
    }

    /**
     * 執行緒執行類,每次存10塊錢
     */
    class NewThread implements Runnable {
        private Bank bank;
        private Semaphore semaphore;

        public NewThread(Bank bank, Semaphore semaphore) {
            this.bank = bank;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            int b = a++;
            if (semaphore.availablePermits() > 0) {
                System.out.println("執行緒" + b + "啟動,進入銀行,有位置立即去存錢");
            } else {
                System.out.println("執行緒" + b + "啟動,進入銀行,無位置,去排隊等待等待");
            }
            try {
                semaphore.acquire();
                bank.save(10);
                System.out.println(b + "賬戶餘額為:" + bank.getAccount());
                Thread.sleep(1000);
                System.out.println("執行緒" + b + "存錢完畢,離開銀行");
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 建立執行緒,呼叫內部類,開始存錢
     */
    public void useThread() {
        Bank bank = new Bank();
        // 定義10個新號量
        Semaphore semaphore = new Semaphore(2);
        // 建立一個快取執行緒池
        ExecutorService es = Executors.newCachedThreadPool();
        // 建立20個執行緒
        for (int i = 0; i < 10; i++) {
            // 執行一個執行緒
            es.submit(new Thread(new NewThread(bank, semaphore)));
        }
        // 關閉執行緒池
        es.shutdown();

        // 從訊號量中獲取兩個許可,並且在獲得許可之前,一直將執行緒阻塞
        semaphore.acquireUninterruptibly(2);
        System.out.println("到點了,工作人員要吃飯了");
        // 釋放兩個許可,並將其返回給訊號量
        semaphore.release(2);
    }

    public static void main(String[] args) {
        SemaphoreThread test = new SemaphoreThread();
        test.useThread();
    }
}

面試題思考: 
    在很多情況下,可能有多個執行緒需要訪問數目很少的資源。假想在伺服器上執行著若干個回答客戶端請求的執行緒。這些執行緒需要連線到同一資料庫,但任一時刻 
    只能獲得一定數目的資料庫連線。你要怎樣才能夠有效地將這些固定數目的資料庫連線分配給大量的執行緒? 
    
答:1.給方法加同步鎖,保證同一時刻只能有一個人去呼叫此方法,其他所有執行緒排隊等待,但是此種情況下即使你的資料庫連結有10個,也始終只有一個處於使

        用狀態。這樣將會大大的浪費系統資源,而且系統的執行效率非常的低下。


    2.另外一種方法當然是使用訊號量,通過訊號量許可與資料庫可用連線數相同的數目,將大大的提高效率和效能。