欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程深入理解

 更新時(shí)間:2021年07月29日 10:38:19   作者:零陵上將軍_xdr  
這篇文章主要介紹了java多線程編程實(shí)例,分享了幾則多線程的實(shí)例代碼,具有一定參考價(jià)值,加深多線程編程的理解還是很有幫助的,需要的朋友可以參考下

多線程

并發(fā)與并行

并發(fā):指兩個(gè)或多個(gè)事件在同一個(gè)時(shí)間段內(nèi)發(fā)生。
并行:指兩個(gè)或多個(gè)事件在同一時(shí)刻發(fā)生(同時(shí)發(fā)生)。

在操作系統(tǒng)中,安裝了多個(gè)程序,并發(fā)指的是在一段時(shí)間內(nèi)宏觀上有多個(gè)程序同時(shí)運(yùn)行,這在單 CPU系統(tǒng)中,每一時(shí)刻只能有一道程序執(zhí)行,即微觀上這些程序是分時(shí)的交替運(yùn)行,只不過(guò)是給人的感覺(jué)是同時(shí)運(yùn)行,那是因?yàn)榉謺r(shí)交替運(yùn)行的時(shí)間是非常短的。

而在多個(gè) CPU 系統(tǒng)中,則這些可以并發(fā)執(zhí)行的程序便可以分配到多個(gè)處理器上(CPU),實(shí)現(xiàn)多任務(wù)并行執(zhí)行, 即利用每個(gè)處理器來(lái)處理一個(gè)可以并發(fā)執(zhí)行的程序,這樣多個(gè)程序便可以同時(shí)執(zhí)行。目前電腦市場(chǎng)上說(shuō)的多核CPU,便是多核處理器,核越多,并行處理的程序越多,能大大的提高電腦運(yùn)行的效率。

線程與進(jìn)程

進(jìn)程:是指一個(gè)內(nèi)存中運(yùn)行的應(yīng)用程序,每個(gè)進(jìn)程都有一個(gè)獨(dú)立的內(nèi)存空間,一個(gè)應(yīng)用程序可以同時(shí)運(yùn)行多 個(gè)進(jìn)程;進(jìn)程也是程序的一次執(zhí)行過(guò)程,是系統(tǒng)運(yùn)行程序的基本單位;系統(tǒng)運(yùn)行一個(gè)程序即是一個(gè)進(jìn)程從創(chuàng) 建、運(yùn)行到消亡的過(guò)程。
線程:線程是進(jìn)程中的一個(gè)執(zhí)行單元,負(fù)責(zé)當(dāng)前進(jìn)程中程序的執(zhí)行,一個(gè)進(jìn)程中至少有一個(gè)線程。一個(gè)進(jìn)程 中是可以有多個(gè)線程的,這個(gè)應(yīng)用程序也可以稱之為多線程程序。 簡(jiǎn)而言之:一個(gè)程序運(yùn)行后至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程中可以包含多個(gè)線程

我們可以再電腦底部任務(wù)欄,右鍵----->打開(kāi)任務(wù)管理器,可以查看當(dāng)前任務(wù)的進(jìn)程:

在這里插入圖片描述

線程調(diào)度:

分時(shí)調(diào)度: 所有線程輪流使用 CPU 的使用權(quán),平均分配每個(gè)線程占用 CPU 的時(shí)間。
搶占式調(diào)度: 優(yōu)先讓優(yōu)先級(jí)高的線程使用 CPU,如果線程的優(yōu)先級(jí)相同,那么會(huì)隨機(jī)選擇一個(gè)(線程隨機(jī)性),Java使用的為搶占式調(diào)度。

創(chuàng)建線程類(lèi)

java所有的線程對(duì)象都必須是Thread類(lèi)或其子類(lèi)的實(shí)例,Java中通過(guò)繼承Thread類(lèi)來(lái)創(chuàng)建并啟動(dòng)多線程的步驟如下:

定義Thread類(lèi)的子類(lèi),并重寫(xiě)該類(lèi)的run()方法,該run()方法的方法體就代表了線程需要完成的任務(wù),因此把 run()方法稱為線程執(zhí)行體。創(chuàng)建Thread子類(lèi)的實(shí)例,即創(chuàng)建了線程對(duì)象調(diào)用線程對(duì)象的start()方法來(lái)啟動(dòng)該線程
public class Demo01 {
    public static void main(String[] args) {
        //創(chuàng)建自定義線程對(duì)象
        MyThread mt = new MyThread("新的線程!");
        //開(kāi)啟新線程
        mt.start();
        //在主方法中執(zhí)行for循環(huán)
        for (int i = 0; i < 10; i++) {
            System.out.println("main線程!"+i);
        }
    }
}
class MyThread extends Thread {
    //定義指定線程名稱的構(gòu)造方法
    public MyThread(String name) {
        //調(diào)用父類(lèi)的String參數(shù)的構(gòu)造方法,指定線程的名稱 
        super(name);
    }
     //重寫(xiě)run方法
    @Override
    public void run() {
        for (int i=0;i<100;i++)
        System.out.println(getName()+":正在執(zhí)行!"+i);
    }
}

線程

Thread類(lèi)

構(gòu)造方法:

public Thread() :分配一個(gè)新的線程對(duì)象。
public Thread(String name) :分配一個(gè)指定名字的新的線程對(duì)象。
public Thread(Runnable target) :分配一個(gè)帶有指定目標(biāo)新的線程對(duì)象。
public Thread(Runnable target,String name) :分配一個(gè)帶有指定目標(biāo)新的線程對(duì)象并指定名字。

常用的方法:

public String getName() :獲取當(dāng)前線程名稱。
public void start() :導(dǎo)致此線程開(kāi)始執(zhí)行; Java虛擬機(jī)調(diào)用此線程的run方法。
public void run() :此線程要執(zhí)行的任務(wù)在此處定義代碼。
public static void sleep(long millis) :使當(dāng)前正在執(zhí)行的線程以指定的毫秒數(shù)暫停(暫時(shí)停止執(zhí)行)。
public static Thread currentThread() :返回對(duì)當(dāng)前正在執(zhí)行的線程對(duì)象的引用。

Runnable接口創(chuàng)建線程

Runnable接口創(chuàng)建線程的步驟:

定義Runnable接口的實(shí)現(xiàn)類(lèi),并重寫(xiě)該接口的run()方法,該run()方法的方法體同樣是該線程的線程執(zhí)行體。創(chuàng)建Runnable實(shí)現(xiàn)類(lèi)的實(shí)例,并以此實(shí)例作為T(mén)hread的target來(lái)創(chuàng)建Thread對(duì)象,該Thread對(duì)象才是真正的線程對(duì)象。調(diào)用線程對(duì)象的start()方法來(lái)啟動(dòng)線程。

代碼案例:

public class Demo2 {
    public static void main(String[] args) {
        //創(chuàng)建自定義類(lèi)對(duì)象 線程任務(wù)對(duì)象
        MyRunnable mr = new MyRunnable();
        // 創(chuàng)建線程對(duì)象
        Thread t = new Thread(mr, "張三");
        t.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("李四 " + i);
        }
    }
}
class MyRunnable implements Runnable{
    @Override public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

通過(guò)實(shí)現(xiàn)Runnable接口,使得該類(lèi)有了多線程類(lèi)的特征。run()方法是多線程程序的一個(gè)執(zhí)行目標(biāo)。所有的多線程代碼都在run方法里面。Thread類(lèi)實(shí)際上也是實(shí)現(xiàn)了Runnable接口的類(lèi)。

在啟動(dòng)的多線程的時(shí)候,需要先通過(guò)Thread類(lèi)的構(gòu)造方法Thread(Runnable target) 構(gòu)造出對(duì)象,然后調(diào)用Thread 對(duì)象的start()方法來(lái)運(yùn)行多線程代碼。

實(shí)際上所有的多線程代碼都是通過(guò)運(yùn)行Thread的start()方法來(lái)運(yùn)行的。因此,不管是繼承Thread類(lèi)還是實(shí)現(xiàn)Runnable接口來(lái)實(shí)現(xiàn)多線程,最終還是通過(guò)Thread的對(duì)象的API來(lái)控制線程的,熟悉Thread類(lèi)的API是進(jìn)行多線程編程的基礎(chǔ)。

Thread和Runnable的區(qū)別

如果一個(gè)類(lèi)繼承Thread,則不適合資源共享。但是如果實(shí)現(xiàn)了Runable接口的話,則很容易的實(shí)現(xiàn)資源共享。

實(shí)現(xiàn)Runnable接口比繼承Thread類(lèi)所具有的優(yōu)勢(shì):

適合多個(gè)相同的程序代碼的線程去共享同一個(gè)資源。可以避免java中的單繼承的局限性。增加程序的健壯性,實(shí)現(xiàn)解耦操作,代碼可以被多個(gè)線程共享,代碼和線程獨(dú)立。線程池只能放入實(shí)現(xiàn)Runable或Callable類(lèi)線程,不能直接放入繼承Thread的類(lèi)。

匿名內(nèi)部類(lèi)方式實(shí)現(xiàn)線程的創(chuàng)建

使用線程的內(nèi)匿名內(nèi)部類(lèi)方式,可以方便的實(shí)現(xiàn)每個(gè)線程執(zhí)行不同的線程任務(wù)操作。 使用匿名內(nèi)部類(lèi)的方式實(shí)現(xiàn)Runnable接口,重新Runnable接口中的run方法:

public class Demo3 {
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 200; i++) {
                    System.out.println("張三 " + i);
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 200; i++) {
                    System.out.println("李四 " + i);
                }
            }
        }.start();
    }
}

線程安全

線程安全

如果有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。程序每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。

案例:游樂(lè)園賣(mài)票

假設(shè)游樂(lè)園要賣(mài)1000張票,一共有3個(gè)賣(mài)票窗口(3個(gè)窗口一起賣(mài)1000張票),采用線程對(duì)象來(lái)模擬;需要票,Runnable接口子類(lèi)來(lái)模擬。

public class Demo4 {
    public static void main(String[] args) {
        //創(chuàng)建線程任務(wù)對(duì)象
        Ticket ticket = new Ticket();
        //創(chuàng)建三個(gè)窗口對(duì)象 
        Thread t1 = new Thread(ticket, "窗口1"); 
        Thread t2 = new Thread(ticket, "窗口2"); 
        Thread t3 = new Thread(ticket, "窗口3"); 
        //同時(shí)賣(mài)票 
        t1.start(); 
        t2.start(); 
        t3.start();
    }
}
class Ticket implements Runnable {
    private int ticket = 1000;
    /** 執(zhí)行賣(mài)票操作 */
    @Override
    public void run() {
        //每個(gè)窗口賣(mài)票的操作
        // 窗口 永遠(yuǎn)開(kāi)啟
        while (true) {
            if (ticket > 0) {
                //有票 可以賣(mài) 使用sleep模擬一下出票時(shí)間
                try {Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto‐generated catch block
                    e.printStackTrace();
                }
                //獲取當(dāng)前線程對(duì)象的名字
                String name = Thread.currentThread().getName();
                System.out.println(name + "正在賣(mài):" + (ticket--));
            }
        }
    }
}

但是結(jié)果會(huì)出先重復(fù)的票和0與-1這樣的錯(cuò)誤票

在這里插入圖片描述

這種問(wèn)題,幾個(gè)窗口(線程)票數(shù)不同步了,這種問(wèn)題稱為線程不安全。

線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的。若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無(wú)寫(xiě)操作,一般來(lái)說(shuō),這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫(xiě)操作,一般都需要考慮線程同步, 否則的話就可能影響線程安全。

線程同步

當(dāng)我們使用多個(gè)線程訪問(wèn)同一資源的時(shí)候,且多個(gè)線程中對(duì)資源有寫(xiě)的操作,就容易出現(xiàn)線程安全問(wèn)題。

要解決上述多線程并發(fā)訪問(wèn)一個(gè)資源的安全性問(wèn)題:也就是解決重復(fù)票與不存在票問(wèn)題,Java中提供了同步機(jī)制 (synchronized)來(lái)解決。

賣(mài)票案例的線程同步簡(jiǎn)述:

窗口1線程進(jìn)入操作的時(shí)候,窗口2和窗口3線程只能在外等著,窗口1操作結(jié)束,窗口1和窗口2和窗口3才有機(jī)會(huì)進(jìn)入代碼 去執(zhí)行。也就是說(shuō)在某個(gè)線程修改共享資源的時(shí)候,其他線程不能修改該資源,等待修改完畢同步之后,才能去搶奪CPU 資源,完成對(duì)應(yīng)的操作,保證了數(shù)據(jù)的同步性,解決了線程不安全的現(xiàn)象。

java完成線程同步的三種方式:

同步代碼塊
同步方法
鎖機(jī)制

同步代碼塊

同步代碼塊: **synchronized **關(guān)鍵字可以用于方法中的某個(gè)區(qū)塊中,表示只對(duì)這個(gè)區(qū)塊的資源實(shí)行互斥訪問(wèn)。

格式:

synchronized( 同步鎖 ) { 
	需要同步操作的代碼 
}

同步鎖:

同步鎖: 對(duì)象的同步鎖只是一個(gè)概念,可以想象為在對(duì)象上標(biāo)記了一個(gè)鎖.。

鎖對(duì)象 可以是任意類(lèi)型。多個(gè)線程對(duì)象 要使用同一把鎖。

同步代碼塊解決賣(mài)票案例:

public class Demo4 {
    public static void main(String[] args) {
        //創(chuàng)建線程任務(wù)對(duì)象
        Ticket ticket = new Ticket();
        //創(chuàng)建三個(gè)窗口對(duì)象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同時(shí)賣(mài)票
        t1.start();
        t2.start();
        t3.start();
    }
}
class Ticket implements Runnable {
    private int ticket = 100;
    /** 執(zhí)行賣(mài)票操作 */
    Object obj=new Object();
    @Override
    public void run() {
        //每個(gè)窗口賣(mài)票的操作
        // 窗口 永遠(yuǎn)開(kāi)啟
        while (true) {
            synchronized (obj)
            {
                if (ticket > 0) {
                    //有票 可以賣(mài) 使用sleep模擬一下出票時(shí)間
                    try {Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto‐generated catch block
                        e.printStackTrace();
                    }
                    //獲取當(dāng)前線程對(duì)象的名字
                    String name = Thread.currentThread().getName();
                    System.out.println(name + "正在賣(mài):" + (ticket--));
                }
            }
        }
    }
}

同步方法

同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執(zhí)行該方法的時(shí)候,其他線程只能在方法外等著。

public synchronized void method(){
	 可能會(huì)產(chǎn)生線程安全問(wèn)題的代碼 
}

使用同步方法解決賣(mài)票案例:

public class Demo5 {
    public static void main(String[] args) {
        //創(chuàng)建線程任務(wù)對(duì)象
        Ticket ticket = new Ticket();
        //創(chuàng)建三個(gè)窗口對(duì)象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同時(shí)賣(mài)票
        t1.start();
        t2.start();
        t3.start();
    }
}
class Ticket implements Runnable {
    private int ticket = 100;
    /**
     * 執(zhí)行賣(mài)票操作
     */
    Object obj = new Object();
    @Override
    public void run() {
        while (true)
        {
            sellticket();
        }
    }
    public synchronized void sellticket() {
        synchronized (obj) {
            if (ticket > 0) {
                //有票 可以賣(mài) 使用sleep模擬一下出票時(shí)間
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto‐generated catch block
                    e.printStackTrace();
                }
                //獲取當(dāng)前線程對(duì)象的名字
                String name = Thread.currentThread().getName();
                System.out.println(name + "正在賣(mài):" + (ticket--));
            }
        }
    }
}

Lock鎖

java.util.concurrent.locks.Lock 機(jī)制提供了比synchronized代碼塊和synchronized方法更廣泛的鎖定操作, 同步代碼塊/同步方法具有的功能Lock都有,除此之外更強(qiáng)大,更體現(xiàn)面向?qū)ο蟆?/p>

public void lock() : 加同步鎖。
public void unlock() : 釋放同步鎖

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo6 {
    public static void main(String[] args) {
        //創(chuàng)建線程任務(wù)對(duì)象
        Ticket ticket = new Ticket();
        //創(chuàng)建三個(gè)窗口對(duì)象
        Thread t1 = new Thread(ticket, "窗口1");
        Thread t2 = new Thread(ticket, "窗口2");
        Thread t3 = new Thread(ticket, "窗口3");
        //同時(shí)賣(mài)票
        t1.start();
        t2.start();
        t3.start();
    }
}
class Ticket implements Runnable {
    private int ticket = 100;
    Lock lock=new ReentrantLock();
    @Override
    public void run() {
        //每個(gè)窗口賣(mài)票的操作
        // 窗口 永遠(yuǎn)開(kāi)啟
        while (true) {
            lock.lock();
            if (ticket > 0) {
                //有票 可以賣(mài) 使用sleep模擬一下出票時(shí)間
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // TODO Auto‐generated catch block
                    e.printStackTrace();
                }
                //獲取當(dāng)前線程對(duì)象的名字
                String name = Thread.currentThread().getName();
                System.out.println(name + "正在賣(mài):" + (ticket--));
            }
            lock.unlock();
        }
    }
}

線程狀態(tài)

當(dāng)線程被創(chuàng)建并啟動(dòng)以后,它既不是一啟動(dòng)就進(jìn)入了執(zhí)行狀態(tài),也不是一直處于執(zhí)行狀態(tài)。在線程的生命周期中, 有幾種狀態(tài)呢?在API中 java.lang.Thread.State 這個(gè)枚舉中給出了六種線程狀態(tài):

線程狀態(tài) 導(dǎo)致?tīng)顟B(tài)發(fā)生條件
NEW(新建) 線程剛被創(chuàng)建,但是并未啟動(dòng)。還沒(méi)調(diào)用start方法
Runnable(可運(yùn)行) 線程可以在java虛擬機(jī)中運(yùn)行的狀態(tài),可能正在運(yùn)行自己代碼,也可能沒(méi)有,這取決于操作系統(tǒng)處理器。
Blocked(鎖阻塞) 當(dāng)一個(gè)線程試圖獲取一個(gè)對(duì)象鎖,而該對(duì)象鎖被其他的線程持有,則該線程進(jìn)入Blocked狀態(tài);當(dāng)該線程持有鎖時(shí),該線程將變成Runnable狀態(tài)。
Waiting(無(wú)限等待) 一個(gè)線程在等待另一個(gè)線程執(zhí)行一個(gè)(喚醒)動(dòng)作時(shí),該線程進(jìn)入Waiting狀態(tài)。進(jìn)入這個(gè)狀態(tài)后是不能自動(dòng)喚醒的,必須等待另一個(gè)線程調(diào)用notify或者notifyAll方法才能夠喚醒。
Timed Waiting(計(jì)時(shí)等待) 同waiting狀態(tài),有幾個(gè)方法有超時(shí)參數(shù),調(diào)用他們將進(jìn)入Timed Waiting狀態(tài)。這一狀態(tài)將一直保持到超時(shí)期滿或者接收到喚醒通知。帶有超時(shí)參數(shù)的常用方法有Thread.sleep 、 Object.wait。
Teminated(被終止) 因?yàn)閞un方法正常退出而死亡,或者因?yàn)闆](méi)有捕獲的異常終止了run方法而死亡。

等待喚醒機(jī)制

線程間通信

概念:多個(gè)線程在處理同一個(gè)資源,但是處理的動(dòng)作(線程的任務(wù))卻不相同。

比如:線程A用來(lái)生成包子的,線程B用來(lái)吃包子的,包子可以理解為同一資源,線程A與線程B處理的動(dòng)作,一個(gè)是生產(chǎn),一個(gè)是消費(fèi),那么線程A與線程B之間就存在線程通信問(wèn)題。

為什么要處理線程間通信:

多個(gè)線程并發(fā)執(zhí)行時(shí), 在默認(rèn)情況下CPU是隨機(jī)切換線程的,當(dāng)我們需要多個(gè)線程來(lái)共同完成一件任務(wù),并且我們希望他們有規(guī)律的執(zhí)行, 那么多線程之間需要一些協(xié)調(diào)通信,以此來(lái)幫我們達(dá)到多線程共同操作一份數(shù)據(jù)。

如何保證線程間通信有效利用資源:

多個(gè)線程在處理同一個(gè)資源,并且任務(wù)不同時(shí),需要線程通信來(lái)幫助解決線程之間對(duì)同一個(gè)變量的使用或操作。 就是多個(gè)線程在操作同一份數(shù)據(jù)時(shí), 避免對(duì)同一共享變量的爭(zhēng)奪。也就是我們需要通過(guò)一定的手段使各個(gè)線程能有效的利用資源。而這種手段即—— 等待喚醒機(jī)制。

等待喚醒機(jī)制

等待喚醒機(jī)制這是多個(gè)線程間的一種協(xié)作機(jī)制。談到線程我們經(jīng)常想到的是線程間的競(jìng)爭(zhēng)(race),比如去爭(zhēng)奪鎖,但這并不是故事的全部,線程間也會(huì)有協(xié)作機(jī)制。 就是在一個(gè)線程進(jìn)行了規(guī)定操作后,就進(jìn)入等待狀態(tài)(wait()), 等待其他線程執(zhí)行完他們的指定代碼過(guò)后 再將 其喚醒(notify());在有多個(gè)線程進(jìn)行等待時(shí), 如果需要,可以使用 notifyAll()來(lái)喚醒所有的等待線程。

wait/notify 就是線程間的一種協(xié)作機(jī)制。

等待喚醒中的方法:

. wait:線程不再活動(dòng),不再參與調(diào)度,進(jìn)入 wait set 中,因此不會(huì)浪費(fèi) CPU 資源,也不會(huì)去競(jìng)爭(zhēng)鎖了,這時(shí)的線程狀態(tài)即是 WAITING。它還要等著別的線程執(zhí)行一個(gè)特別的動(dòng)作,也即是“通知(notify)”在這個(gè)對(duì)象上等待的線程從wait set 中釋放出來(lái),重新進(jìn)入到調(diào)度隊(duì)列(ready queue)中
notify:則選取所通知對(duì)象的 wait set 中的一個(gè)線程釋放;例如,餐館有空位置后,等候就餐最久的顧客最先入座。
notifyAll:則釋放所通知對(duì)象的 wait set 上的全部線程。

生產(chǎn)者與消費(fèi)者問(wèn)題

等待喚醒機(jī)制其實(shí)就是經(jīng)典的“生產(chǎn)者與消費(fèi)者”的問(wèn)題。 就拿生產(chǎn)包子消費(fèi)包子來(lái)說(shuō)等待喚醒機(jī)制如何有效利用資源:
包子鋪線程生產(chǎn)包子,吃貨線程消費(fèi)包子。當(dāng)包子沒(méi)有時(shí)(包子狀態(tài)為false),吃貨線程等待,包子鋪線程生產(chǎn)包子 (即包子狀態(tài)為true),并通知吃貨線程(解除吃貨的等待狀態(tài)),因?yàn)橐呀?jīng)有包子了,那么包子鋪線程進(jìn)入等待狀態(tài)。 接下來(lái),吃貨線程能否進(jìn)一步執(zhí)行則取決于鎖的獲取情況。如果吃貨獲取到鎖,那么就執(zhí)行吃包子動(dòng)作,包子吃完(包 子狀態(tài)為false),并通知包子鋪線程(解除包子鋪的等待狀態(tài)),吃貨線程進(jìn)入等待。包子鋪線程能否進(jìn)一步執(zhí)行則取 決于鎖的獲取情況。

public class Demo7 {
    public static void main(String[] args) {
        baozi bz=new baozi();
        new baozipu(bz).start();
        new chihuo(bz).start();
    }
}
class baozi{
    String pi;
    String xian;
    boolean flag=false;
}
class baozipu extends Thread {
    private baozi bz;
    public baozipu(baozi bz) {
        this.bz=bz;
    }
    @Override
    public void run() {
        int count=0;
        while (true){
            synchronized(bz)
            {
                if(bz.flag)
                {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(count%2==0){
                    //生產(chǎn) 大蝦餡包子
                    bz.pi = "薄皮";
                    bz.xian = "大蝦陷";
                }else{
                    //生產(chǎn) 冰皮 羊肉大蔥陷
                    bz.pi = "冰皮";
                    bz.xian = "羊肉大蔥陷";
                }
                count++;
                System.out.println("包子鋪正在生產(chǎn):"+bz.pi+bz.xian+"包子");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                bz.flag=true;
                System.out.println("包子鋪已經(jīng)生產(chǎn)好了:"+bz.pi+bz.xian+"包子,吃貨可以開(kāi)始吃了");
                bz.notify();
            }
        }
    }
}
class chihuo extends Thread{
    private baozi bz;
    public chihuo(baozi bz)
    {
        this.bz=bz;
    }
    @Override
    public void run() {
        while (true)
        {
            synchronized (bz){
                if (!bz.flag)
                {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃貨正在吃:"+bz.pi+bz.xian+"的包子");
                //吃貨吃完包子
                //修改包子的狀態(tài)為false沒(méi)有
                bz.flag = false;
                //吃貨喚醒包子鋪線程,生產(chǎn)包子
                bz.notify();
                System.out.println("吃貨已經(jīng)把:"+bz.pi+bz.xian+"的包子吃完了,包子鋪開(kāi)始生產(chǎn)包子");
                System.out.println("----------------------------------------------------");
            }
        }
    }
}

線程池

線程池的概念

線程池:其實(shí)就是一個(gè)容納多個(gè)線程的容器,其中的線程可以反復(fù)使用,省去了頻繁創(chuàng)建線程對(duì)象的操作, 無(wú)需反復(fù)創(chuàng)建線程而消耗過(guò)多資源。

合理利用線程池能夠帶來(lái)三個(gè)好處:

  • 降低資源消耗。減少了創(chuàng)建和銷(xiāo)毀線程的次數(shù),每個(gè)工作線程都可以被重復(fù)利用,可執(zhí)行多個(gè)任務(wù)。
  • 提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。
  • 提高線程的可管理性。可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橄倪^(guò)多的內(nèi) 存,而把服務(wù)器累趴下(每個(gè)線程需要大約1MB內(nèi)存,線程開(kāi)的越多,消耗的內(nèi)存也就越大,最后死機(jī))。

線程池的使用

Java里面線程池的頂級(jí)接口是 java.util.concurrent.Executor ,但是嚴(yán)格意義上講 Executor 并不是一個(gè)線程 池,而只是一個(gè)執(zhí)行線程的工具。真正的線程池接口是 java.util.concurrent.ExecutorService

要配置一個(gè)線程池是比較復(fù)雜的,尤其是對(duì)于線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優(yōu) 的,因此在 java.util.concurrent.Executors 線程工廠類(lèi)里面提供了一些靜態(tài)工廠,生成一些常用的線程池。官 方建議使用Executors工程類(lèi)來(lái)創(chuàng)建線程池對(duì)象。

Executors類(lèi)中有個(gè)創(chuàng)建線程池的方法如下:

public static ExecutorService newFixedThreadPool(int nThreads) :返回線程池對(duì)象。(創(chuàng)建的是有界線 程池,也就是池中的線程個(gè)數(shù)可以指定最大數(shù)量)

獲取到了一個(gè)線程池ExecutorService 對(duì)象,那么怎么使用呢,在這里定義了一個(gè)使用線程池對(duì)象的方法如下:

public Future<?> submit(Runnable task) :獲取線程池中的某一個(gè)線程對(duì)象,并執(zhí)行
Future接口:用來(lái)記錄線程任務(wù)執(zhí)行完畢后產(chǎn)生的結(jié)果。線程池創(chuàng)建與使用。

使用線程池中線程對(duì)象的步驟:

創(chuàng)建線程池對(duì)象。創(chuàng)建Runnable接口子類(lèi)對(duì)象。(task)提交Runnable接口子類(lèi)對(duì)象。(take task)關(guān)閉線程池(一般不做)。

案例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo8 {
    public static void main(String[] args) {
        ExecutorService es= Executors.newFixedThreadPool(3);
        MyRunable r=new MyRunable();
        es.submit(r);
        es.submit(r);
        es.submit(r);
        es.submit(r);
    }
}
class MyRunable implements Runnable{
    @Override
    public void run() {
        System.out.println("我是一個(gè)廚師,我去做飯了");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("廚師做好飯了:"+Thread.currentThread().getName());

    }
}

總結(jié)

本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

最新評(píng)論