Java 并發(fā)專題:Semaphore 實(shí)現(xiàn) 互斥 與 連接池

Semaphore,位于java.util.concurrent包下面

  • Semaphore中管理著一組虛擬的許可,許可的初始數(shù)量可通過構(gòu)造函數(shù)來指定new Semaphore(1);,執(zhí)行操作時可以首先獲得許可semaphore.acquire();,并在使用后釋放許可semaphore.release();。如果沒有許可,那么acquire方法將會一直阻塞直到有許可(或者直到被終端或者操作超時)。
  • Semaphore是一種在多線程環(huán)境下使用的設(shè)施,該設(shè)施負(fù)責(zé)協(xié)調(diào)各個線程,以保證它們能夠正確、合理的使用公共資源的設(shè)施,也是操作系統(tǒng)中用于控制進(jìn)程同步互斥的量。
  • Semaphore分為單值和多值兩種,前者只能被一個線程獲得,后者可以被若干個線程獲得。
    作用:可以用來控制同時訪問某個特定資源的操作數(shù)量,或者某個操作的數(shù)量。

下面使用Semaphore實(shí)現(xiàn)兩個例子:

  1. 互斥
    大家都學(xué)過操作系統(tǒng),都知道互斥的概念,比較簡單的互斥實(shí)現(xiàn),比如PV操作,判斷資源,然后忙等實(shí)現(xiàn)互斥;上一篇也說過,忙等對CPU的消耗巨大,下面我們通過Semaphore來實(shí)現(xiàn)一個比較好的互斥操作:
    假設(shè)我們公司只有一臺打印機(jī),我們需要對這臺打印機(jī)的打印操作進(jìn)行互斥控制:
    首先是沒有使用Semaphore來實(shí)現(xiàn)互斥操作
public class Test {
    public void print(String str) throws InterruptedException {
        System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成,開始打印...");
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
        System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
    }

    public static void main(String[] args) {
        Test t = new Test();
        
        for(int i = 0; i < 10 ; i++) {
            new Thread() {
                public void run() {
                    try {
                        t.print("hello");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

運(yùn)行結(jié)果:

沒有Semaphore

從結(jié)果可以看出,一個Thread還沒有打印完成,退出程序,別的Thread就進(jìn)入開始打印了,這樣必然沒有按照正確的流程來打印。

使用Semaphore來控制互斥訪問數(shù)

import java.util.concurrent.Semaphore;
/**
 * 使用Semphore實(shí)現(xiàn)互斥訪問打印機(jī)
 * @author ghw
 *
 */
public class Test {
    //定義初始值為1的信號量 
    private Semaphore semaphore = new Semaphore(1);
    //模擬打印機(jī)打印操作
    public void print(String str) throws InterruptedException {
        //請求許可
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName()+"準(zhǔn)備完成,開始打印...");
        //模擬打印時間
        Thread.sleep(1000);
        System.out.println(Thread.currentThread().getName()+"正在打印..."+str);
        System.out.println(Thread.currentThread().getName()+"打印完成,退出程序 ...");
        //釋放許可
        semaphore.release();
    }

    public static void main(String[] args) {
        Test t = new Test();
        //開啟10個線程執(zhí)行打印任務(wù)
        for(int i = 0; i < 10 ; i++) {
            new Thread() {
                public void run() {
                    try {
                        t.print("hello");
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }.start();
        }
    }
}

運(yùn)行結(jié)果:

使用Semphore

從結(jié)果可以看出,多個線程雖然是無序執(zhí)行的,但每個線程執(zhí)行完成后退出程序才開始執(zhí)行下一個線程,這樣就不會亂。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class ConnectPool {
    
    private final List<Conn> pool = new ArrayList<Conn>(3);  
    private Semaphore semaphore = new Semaphore(3);

    
    public ConnectPool() {
        pool.add(new Conn());
        pool.add(new Conn());
        pool.add(new Conn());
    }
    
    public Conn getConn() throws InterruptedException {
        semaphore.acquire();  
        Conn c = null  ;  
        synchronized (pool)  
        {  
            c = pool.remove(0);  
        }  
        System.out.println(Thread.currentThread().getName()+" 獲得一個連接 " + c);  
        return c ;  
    }
    
    public void release(Conn c) {
        pool.add(c);  
        System.out.println(Thread.currentThread().getName()+" 釋放了一個連接 " + c);  
        semaphore.release();  
    }
    
    public static void main(String[] args) {
        ConnectPool pool = new ConnectPool();
        
        new Thread() {
            public void run() {
                try {
                    Conn c = pool.getConn();
                    Thread.sleep(3000);
                    pool.release(c);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();
        
        for(int i=0;i<5;i++) {
             new Thread() {
                public void run() {
                    try {
                        Conn c = pool.getConn();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                };
                
             }.start();
        }
    }
}

運(yùn)行結(jié)果:

運(yùn)行結(jié)果3

先讓Thread-0持有一個連接3秒,然后瞬間讓3個線程再去請求分配連接,造成Thread-3一直等到Thread-0對連接的釋放,然后獲得連接。

通過兩個例子,基本已經(jīng)了解了Semaphore的用法,這里的線程池例子只是為了說明Semaphore的用法,真實(shí)的實(shí)現(xiàn)代碼比這復(fù)雜的多,而且可能也不會直接用Semaphore。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務(wù))結(jié)合為一個獨(dú)立的整體,并盡...
    Jayden_Cao閱讀 2,247評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,568評論 19 139
  • 一、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)。 NEW:這種情況指的是,通過 New 關(guān)鍵字創(chuàng)...
    Java旅行者閱讀 4,865評論 0 44
  • 本文章轉(zhuǎn)載于搜狗測試 一、什么是API接口測試? API接口有多種,個人將其劃分為三類。 第一種是函數(shù)級別的,測試...
    夜境閱讀 1,426評論 1 2
  • 有些人一直在你的記憶當(dāng)中,不是他對你有多么的重要,也不是你永遠(yuǎn)都放不下他,只是單純的因?yàn)?,他陪你走過的那一段路,是...
    婉彤Sharon閱讀 651評論 0 0

友情鏈接更多精彩內(nèi)容