2021-05-07

線程安全的類

StringBuffer

  • 線程安全,可變的字符序列
  • 從版本JDK5開始,被StringBuilder替代。通常應(yīng)該使用StringBuilder類,因為它支持所有相同的操作,但它更快,因為它不執(zhí)行同步

Vector

  • 從Java2平臺v1.2開始,該類改進(jìn)了List接口,使其成為Java Collections Framework的成員。與新的集合實現(xiàn)不同,Vector被同步。如果不需要線程安全的實現(xiàn),建議使用ArrayList代替Vector

Hashtable:

  • 該類實現(xiàn)了一個哈希表,它將鍵映射到值。任何非null對象都可以用作鍵或者值
  • 從Java 2平臺v1.2 開始,該類進(jìn)行了改進(jìn),實現(xiàn)了Map接口,使其成為Java Collections Framework的成員。與新的集合實現(xiàn)不同,Hashtable被同步。如果不需要線程安全的實現(xiàn),,建議使用HashMap代替Hashtable

Lock鎖

為了更清晰的表達(dá)如何加鎖和釋放鎖,JDK5以后提供了一個新的鎖對象Lock

  • void lock():獲得鎖
  • void unlock():釋放鎖
    Lock是接口不能直接實例化,這里采用它的實現(xiàn)類ReentrantLock來實例化
    ReentrantLock的構(gòu)造方法
  • ReentrantLock():創(chuàng)建一個ReentrantLock的實例
public class SellTicket implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            try {
                lock.lock();
                if(tickets>0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+tickets+"張票");
                    tickets--;
                }
            } finally {
                lock.unlock();
            }

        }
    }
}

生產(chǎn)者消費者模式概述

生產(chǎn)者消費者模式是一個十分經(jīng)典的多線程協(xié)作的模式,弄懂生產(chǎn)者消費者問題能夠讓我們對多線程編程的理解更加深刻所謂生產(chǎn)者消費者問題,實際上主要是包含了兩類線程:

  • 一類是生產(chǎn)者線程用于生產(chǎn)數(shù)據(jù)
  • 一類是消費者線程用于消費數(shù)據(jù)

為了解耦生產(chǎn)者和消費者的關(guān)系,通常會采用共享數(shù)據(jù)區(qū)域,就像是一個倉庫

  • 生產(chǎn)者生產(chǎn)數(shù)據(jù)之后直接放置在共享數(shù)據(jù)區(qū)中,并不需要關(guān)心消費者的行為
  • 消費者只需要從共享數(shù)據(jù)區(qū)中去獲取數(shù)據(jù),并不需要關(guān)心生產(chǎn)者的行為

為了體現(xiàn)生產(chǎn)和消費過程中的等待和喚醒,Java就提供了幾個方法供我們使用,這幾個方法在Object類中
Object類的等待和喚醒方法:

方法名 說明
void wait() 導(dǎo)致當(dāng)前線程等待,直到另一個線程調(diào)用該對象的notify()方法或notifyAll()方法
void notify() 喚醒正在等待對象監(jiān)視器的單個線程
void notifyAll() 喚醒正在等待對象監(jiān)視器的所有線程

生產(chǎn)者消費者案例

生產(chǎn)者消費者案例中包含的類:

  • 奶箱類(Box):定義一個成員變量,表示第x瓶奶,提供存儲牛奶和獲取牛奶的操作
  • 生產(chǎn)者類(Producer):實現(xiàn)Runnable接口,重寫run()方法,調(diào)用存儲牛奶的操作
  • 消費者類(Customer):實現(xiàn)Runnable接口,重寫run()方法,調(diào)用獲取牛奶的操作
  • 測試類(BoxDemo):里面有main方法,main方法中的代碼步如下:
  1. 創(chuàng)建奶箱對象,這是共享數(shù)據(jù)區(qū)域
  2. 創(chuàng)建生產(chǎn)者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞,因為在這個類中要調(diào)用存儲牛奶的操作
  3. 創(chuàng)建消費者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞,因為在這個類中要調(diào)用獲取牛奶的操作
  4. 創(chuàng)建兩個線程對象,分別把生產(chǎn)者對象和消費者對象作為構(gòu)造方法參數(shù)傳遞
  5. 啟動線程
生產(chǎn)者
public class Producer implements Runnable {
    private Box b;

    public Producer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            b.put(i);
        }
    }
}
消費者
public class Customer implements Runnable{
    private Box b; // 定義一個成員變量

    public Customer(Box b) {
        this.b = b;
    }

    @Override
    public void run() {
        while (true) {
            b.get();
        }
    }
}
奶箱類
public class Box {
    // 定義一個成員變量,表示第x瓶奶
    private int milk;

    // 定義一個成員變量,表示奶箱的狀態(tài)
    private boolean state = false;

    public synchronized void put(int milk) {
        // 如果有牛奶,等待消費
        if(state) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果沒有牛奶,就生產(chǎn)牛奶
        this.milk = milk;
        System.out.println("送奶工將第" + this.milk + "瓶奶放入奶箱");
        // 生產(chǎn)完畢之后,修改奶箱狀態(tài)
        state = true;
        // 喚醒其他等待的線程
        notifyAll();
    }

    public synchronized void get() {
        // 如果沒有牛奶,等待生產(chǎn)
        if(!state){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果有牛奶,就消費牛奶
        System.out.println("用戶拿到第"+this.milk +"瓶奶") ;
        // 消費完畢之后,修改奶箱狀態(tài)
        state = false;
        // 喚醒其他等待的線程
        notifyAll();
    }
}
測試類
public class BoxDemo {
    public static void main(String[] args) {
        // 創(chuàng)建奶箱對象,這是共享數(shù)據(jù)區(qū)域
        Box b = new Box();
        // 創(chuàng)建生產(chǎn)者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞,因為在這個類中要調(diào)用存儲牛奶的操作
        Producer p = new Producer(b);
        // 創(chuàng)建消費者對象,把奶箱對象作為構(gòu)造方法參數(shù)傳遞,因為在這個類中要調(diào)用獲取牛奶的操作
        Customer c = new Customer(b);
        // 創(chuàng)建兩個線程對象,分別把生產(chǎn)者對象和消費者對象作為構(gòu)造方法參數(shù)傳遞
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(c);

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

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

  • 銀行網(wǎng)點服務(wù)營銷一體化咨詢培訓(xùn)項目 一、項目背景 網(wǎng)點是拓展個人...
    師資供應(yīng)閱讀 214評論 0 0
  • 進(jìn)程和線程的區(qū)別1、進(jìn)程是資源分配和調(diào)度的基本單位,是計算機中的程序?qū)δ硵?shù)據(jù)集合上的一次運行活動,是線程的容器,也...
    垂直居中的句號閱讀 73評論 0 1
  • 一、實現(xiàn)多線程 1.1進(jìn)程和線程【理解】 進(jìn)程:是正在運行的程序? 是系統(tǒng)進(jìn)行資源分配和調(diào)用的獨立單位? 每一個進(jìn)...
    super_hongtao閱讀 249評論 0 0
  • 我是黑夜里大雨紛飛的人啊 1 “又到一年六月,有人笑有人哭,有人歡樂有人憂愁,有人驚喜有人失落,有的覺得收獲滿滿有...
    陌忘宇閱讀 8,861評論 28 54
  • 信任包括信任自己和信任他人 很多時候,很多事情,失敗、遺憾、錯過,源于不自信,不信任他人 覺得自己做不成,別人做不...
    吳氵晃閱讀 6,377評論 4 8

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