第12篇-JAVA 多線程

2022-03-11 11:29 更新
  • 每篇一句 :不要只看到艱難,要看艱難后面的勝利
  • 初學(xué)心得: 敢于嘗試,就等于你已經(jīng)向成功邁出了第一步
  • (筆者:JEEP/711)[JAVA筆記 | 時(shí)間:2017-04-20| JAVA 多線程 ]

1.進(jìn)程與線程

1.什么是進(jìn)程
- 程序是指令和數(shù)據(jù)的有序的集合,其本身沒有任何運(yùn)行的含義,是一個(gè)靜態(tài)的概念
- 進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序,一個(gè)實(shí)體
- 幾乎所有的操作系統(tǒng)都支持同時(shí)運(yùn)行多個(gè)任務(wù),一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程
- 當(dāng)一個(gè)程序運(yùn)行時(shí),內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順序執(zhí)行流就是一個(gè)線程

2.進(jìn)程的狀態(tài): 進(jìn)程執(zhí)行時(shí)的間斷性,決定了進(jìn)程可能具有多種狀態(tài),事實(shí)上,運(yùn)行中的進(jìn)程具有
- 以下三種基本狀態(tài):
- 1.就緒狀態(tài)(Ready)
- 2.運(yùn)行狀態(tài)(Running)
- 3.阻塞狀態(tài)(Blocked)

3.線程
- 線程實(shí)際上是進(jìn)程基礎(chǔ)之上的進(jìn)一步劃分,一個(gè)進(jìn)程啟動之后,里面的若干程序,又可以劃分成若干個(gè)線程
- 線程:是進(jìn)程中的一個(gè)執(zhí)行路徑,共享一個(gè)內(nèi)存空間,線程之間可以自由切換
- 并發(fā)執(zhí)行,一個(gè)進(jìn)程最少有一個(gè)線程(單線程程序)

4.線程實(shí)現(xiàn)的兩種方式
- 在java中如果想要實(shí)現(xiàn)多線程操作,兩種實(shí)現(xiàn)方法
- 1.一種是繼承Thrcad類
- 2.另一種是實(shí)現(xiàn)Runnable接口

5.多線程編程的優(yōu)勢
- 進(jìn)程間不能共享內(nèi)存,但線程之間共享內(nèi)存非常容易
- 系統(tǒng)創(chuàng)建進(jìn)程需要為該進(jìn)程重新分配系統(tǒng)資源,但創(chuàng)建線程則代價(jià)小得多,因此使用多線程來實(shí)現(xiàn)多任務(wù)并發(fā)比多進(jìn)程的效率高
- Java語言內(nèi)置的多線程功能支持,而不是單純地作為底層操作系統(tǒng)的調(diào)度方式,從而簡化了Java的多線程編程

2.繼承Thread類創(chuàng)建線程類

(1)定義Thread類的子類,并重寫該類的run方法,該run方法的方法體就是代表了線程需要完成的任務(wù)
因此,我們經(jīng)常把run方法稱為線程執(zhí)行體
(2)創(chuàng)建Thread子類的實(shí)例,即創(chuàng)建了線程對象
(3)調(diào)用線程對象的start方法來啟動該線程
(4)join線程:Thread提供了讓一個(gè)線程等待另一個(gè)線程完成的方法:join() 方法
當(dāng)在某個(gè)程序執(zhí)行流中調(diào)用其他線程的join()方法時(shí),調(diào)用線程將被阻塞,直到被join方法加入的join線程完成為止
join()方法通常由使用線程的程序調(diào)用,以將大問題劃分成許多小問題,每個(gè)小問題分配一個(gè)線程。當(dāng)所有的小問題都得到處理后,再調(diào)用主線程來進(jìn)一步操作

3.實(shí)現(xiàn)Runnable接口創(chuàng)建線程類

(1)定義Runnable接口的實(shí)現(xiàn)類,并重寫該接口的run方法,該run方法的方法體同樣是該線程的線程執(zhí)行體
(2)創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為Thread的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象
(3)調(diào)用線程對象的start方法來啟動該線程

4.線程睡眠

如果我們需要讓當(dāng)前正在執(zhí)行的線程暫停一段時(shí)間,并進(jìn)入阻塞狀態(tài)
則可以通過調(diào)用Thread類的靜態(tài)sleep方法,sleep方法有兩種重載的形式:
- static void sleep(long millis):讓當(dāng)前正在執(zhí)行的線程暫停millis毫秒,并進(jìn)入阻塞狀態(tài),該方法受到系統(tǒng)計(jì)時(shí)器和線程調(diào)度器的精度和準(zhǔn)確度的影響
- static void sleep(long millis, int nanos):讓當(dāng)前正在執(zhí)行的線程暫停millis毫秒加nanos毫微妙,并進(jìn)入阻塞狀態(tài),該方法受到系統(tǒng)計(jì)時(shí)器和線程調(diào)度器的精度和準(zhǔn)確度的影響

5.兩種線程方式的對比

采用實(shí)現(xiàn)Runnable接口方式的多線程:
- 線程類只是實(shí)現(xiàn)了Runnable接口,還可以可以繼承其他類
- 在這種方式下,可以多個(gè)線程共享同一個(gè)target對象,所以非常適合多個(gè)相同線程來處理同一份資源的情況
- 從而可以將CPU,代碼和數(shù)據(jù)分開,形成清晰的模型,較好地體現(xiàn)了面向?qū)ο蟮乃枷?
- 劣勢:編程稍稍復(fù)雜,如果需要訪問當(dāng)前線程,必須使用Thread.currentThread()方法

采用繼承Thread類方式的多線程:
- 劣勢:因?yàn)榫€程類已經(jīng)繼承了Thread類,所以不能再繼承其他父類
- 優(yōu)勢:編寫簡單,如果需要訪問當(dāng)前線程,無需使用Thread.currentThread()方法,直接使用this即可獲得當(dāng)前線程

6.線程生命周期

線程聲明周期:
- 就緒狀態(tài)
- 執(zhí)行狀態(tài)
- 阻塞狀態(tài)

7.線程安全問題

多條線程并發(fā)修改共享資源就容易引發(fā)線程安全問題
使用同步解決多線程共享數(shù)據(jù)出現(xiàn)安全問題
多個(gè)線程有可能同時(shí)處理同一個(gè)資源
線程同步就是指多個(gè)線程在同一個(gè)時(shí)間段內(nèi)只能有一個(gè)線程執(zhí)行指定代碼
其他線程等待此線程完成之后才可以繼續(xù)執(zhí)行
同步代碼塊synchronized(要同步對象){要同步的操作}
同步方法public synchronized void method(){要同步的操作}
同步代碼會帶來性能降低的問題,提高數(shù)據(jù)的安全性,犧牲性能來保證安全
同步代碼塊
Java的多線程支持引入了同步監(jiān)視器來解決這個(gè)問題,使用同步監(jiān)視器的通用方法就是同步代碼塊
synchronized后括號里的obj就是同步監(jiān)視器,上面代碼的含義是:線程開始執(zhí)行同步代碼塊之前,必須先獲得對同步監(jiān)視器的鎖定
同步方法
Java的多線程安全支持還提供了同步方法,同步方法就是使用synchronized關(guān)鍵字來修飾某個(gè)方法,則該方法成為同步方法
對于同步方法而言,無需顯式指定同步監(jiān)視器,同步方法的同步監(jiān)視器是this,也就是該對象本身
線程死鎖
過多的同步有可能死鎖,死鎖的操作一般是在程序運(yùn)行的時(shí)候才有可能出現(xiàn)
多線程中要進(jìn)行資源的共享,就需要同步,但同步過多,就可能造成死鎖

8.線程創(chuàng)建啟動實(shí)例


<br> package cn.jeep;
<br> //繼承Thread類
<br> public class XianChengDemo extends Thread{
<br>    private int i;//私有成員屬性
<br>    //重寫run方法,run方法的方法體就是線程執(zhí)行體
<br>    public void run(){
<br>        for(;i<100;i++){
<br>            //當(dāng)線程類繼承Thread類時(shí),直接使用this.及格獲取當(dāng)前線程
<br>            //Thread對象的getNname()返回當(dāng)前線程名字
<br>            //因此可以直接調(diào)用getName方法返回當(dāng)前線程名字
<br>            System.out.println(getName()+" "+i);
<br>        }
<br>    }
<br>    //主方法入口
<br>    public static void main(String[] args) {
<br>        for(int i=0;i<100;i++){
<br>            //調(diào)用Thread的currenTread方法獲取當(dāng)前線程
<br>            System.out.println(Thread.currentThread().getName()+" "+i);
<br>            if(i == 20){
<br>                //創(chuàng)建并啟動第一個(gè)線程
<br>                new XianChengDemo().start();
<br>                //創(chuàng)建并啟動第二個(gè)線程
<br>                new XianChengDemo().start();    
<br>            }
<br>        }
<br>    }
<br> }
<br> ```
<br>```
<br> package cn.runnable;
<br> //聲明一個(gè)類
<br> public  class RunnableDenmo implements Runnable{
<br>    private int i;//私有屬性
<br>    //重寫run方法
<br>    public void run(){
<br>        for(;i<100;i++){
<br>            //當(dāng)線程類實(shí)現(xiàn)Runnable接口時(shí)
<br>            //如果想獲取當(dāng)前線程,只能用Thread.currentTread()方法
<br>            System.out.println(Thread.currentThread().getName()+" "+i);
<br>        }
<br>    }
<br>    //主方法
<br>    public static void main(String[] args) {
<br>        for(int i=0;i<100;i++){
<br>            System.out.println(Thread.currentThread().getName()+" "+i);
<br>            if(i == 20){
<br>                RunnableDenmo st = new RunnableDenmo();
<br>                //通過new Thread(target,name)方法創(chuàng)建新線程
<br>                new Thread(st,"新線程1").start();
<br>                new Thread(st,"新線程2").start();
<br>            }
<br>        }
<br>    }
<br> }
<br>```
<br>```
<br> package cn.callable;
<br> import java.util.concurrent.Callable;
<br> import java.util.concurrent.FutureTask;
<br> public class CallableDemo {
<br>    public static void main(String[] args) {
<br>        //創(chuàng)建Callable對象
<br>        @SuppressWarnings("unused")
<br>        CallableDemo cl = new CallableDemo();
<br>        //先使用Lambda表達(dá)式創(chuàng)建Callable<Integer&對象
<br>        //使用FutureTask來包裝Callable對象
<br>        FutureTask<Integer& task = new FutureTask<Integer&((Callable<Integer&)()-&{
<br>            int i = 0;
<br>            for(;i<100;i++){
<br>                System.out.println(Thread.currentThread().getName()+"的循環(huán)變量的i的值:"+i);
<br>            }
<br>            //call()方法可以有返回值
<br>            return i;
<br>        });
<br>        for(int i=0;i<100;i++){
<br>            if(i==20){
<br>                //實(shí)質(zhì)還是以Callable對象來創(chuàng)建并啟動線程
<br>                new Thread(task,"有返回值得線程").start();
<br>            }
<br>        }
<br>        try{
<br>        //獲取線程返回值
<br>        System.out.println("子線程的返回值:"+task.get());
<br>            }catch(Exception ex){
<br>        ex.printStackTrace();
<br>        }
<br>    }
<br> }
<br>```

9.線程同步實(shí)例


<br> package cn.tongbu;
<br> public class TB {
<br>    public static void main(String[] args) {
<br>        TbDemo tb = new TbDemo(); 
<br>        Thread t1 = new Thread(tb,"小明");
<br>        Thread t2 = new Thread(tb,"小白");
<br>        Thread t3 = new Thread(tb,"小紅");
<br>        Thread t4 = new Thread(tb,"小黑");
<br>        Thread t5 = new Thread(tb,"小華");
<br>        t1.start();
<br>        t2.start();
<br>        t3.start();
<br>        t4.start();
<br>        t5.start();
<br>    }
<br> }
<br> class TbDemo implements Runnable{
<br>    Object obj = new Object();//同步得標(biāo)記
<br>    @Override
<br>    public void run() {
<br>        //線程同步代碼塊
<br>        //synchronized(obj){
<br>        //}
<br>        say();//調(diào)用同步方法
<br>    }
<br>    /**
<br>     * 同步方法
<br>     */
<br>    public synchronized void say(){
<br>        System.out.println(Thread.currentThread().getName()+"正在通話中....");
<br>        try {
<br>            Thread.sleep(2000);//2秒
<br>        } catch (InterruptedException e) {
<br>            e.printStackTrace();
<br>        }
<br>        System.out.println(Thread.currentThread().getName()+"通話結(jié)束!");
<br>    }
<br> }
<br> ```

10.線程死鎖實(shí)例


<br> package cn.sisuo;
<br> public class SiSuoDemo {
<br>    public static void main(String[] args) {
<br>        new Ss();
<br>    }
<br> }
<br> //警
<br> class XingJing{
<br>    //同步方法
<br>    public synchronized void say(FeiTu f){
<br>        System.out.println("警方說先放人質(zhì),在實(shí)行抓捕");
<br>        f.dos();
<br>    }
<br>    public synchronized void dos(){
<br>        System.out.println("同意匪徒要求,在實(shí)行抓捕");
<br>    }
<br> }
<br> //匪徒
<br> class FeiTu{
<br>    //同步方法
<br>    public synchronized void say(XingJing x){
<br>        System.out.println("匪徒說先找一輛車,在放人質(zhì)");
<br>        x.dos();
<br>    }
<br>    public synchronized void dos(){
<br>        System.out.println("先找一輛車,再放人質(zhì)");
<br>    }
<br> }
<br> /**
<br>  * 死鎖線程
<br>  * @author JEEP-711
<br>  *
<br>  */
<br> class Ss implements Runnable{
<br>    XingJing xj = new XingJing();
<br>    FeiTu ft = new FeiTu();
<br>    //模擬線程死鎖構(gòu)造方法
<br>    public Ss(){
<br>        new Thread(this).start();//為了快速實(shí)現(xiàn)
<br>        ft.say(xj);
<br>    }
<br>    @Override
<br>    public void run() {
<br>        xj.say(ft);
<br>    }
<br> }
<br> ```

11.多線程同步銀行取錢實(shí)例


<br> package cn.chinabank;//中國銀行包
<br> //聲明一個(gè)賬戶類
<br> public class Account {
<br>    private String name;//私有屬性-姓名
<br>    private double number=0.0;//私有屬性-余額
<br>    //構(gòu)造方法并傳值
<br>    public Account(String name,double number){
<br>        this.name = name;//當(dāng)前姓名
<br>        this.number = number;//當(dāng)前金額
<br>    }
<br>    //取得getName方法
<br>    public String getName() {
<br>        return name;
<br>    }
<br>    //設(shè)置setName方法
<br>    public void setName(String name) {
<br>        this.name = name;
<br>    }
<br>    //取得getNumber方法
<br>    public double getNumber() {
<br>        return number;
<br>    }
<br>    //設(shè)置setNumber方法
<br>    public void setNumber(double number) {
<br>        this.number = number;
<br>    }
<br> }
<br> ```
<br> ```
<br> package cn.chinabank;//中國銀行包
<br> import java.util.Scanner;
<br> //聲明一個(gè)中國銀行類
<br> public class ChinaBank {
<br>    //定義主方法
<br>    public static void main(String[] args) {
<br>        @SuppressWarnings("resource")
<br>        Scanner sc = new Scanner(System.in);//接收控制臺鍵盤輸入
<br>        System.out.println("----------------------");
<br>        System.out.println("----歡迎您進(jìn)入中國銀行-----");
<br>        System.out.println("---請選擇您需要辦理的業(yè)務(wù)---");
<br>        System.out.println("1.存款"+"\t"+" 2.取款"+"\t"+"3.退出");
<br>        System.out.println("----------------------");
<br>        int sr = sc.nextInt();//接收一個(gè)數(shù)
<br>        //接收用戶輸入
<br>        switch(sr){
<br>        //輸入1則進(jìn)入存款功能
<br>            case 1:
<br>                 System.out.println("請您輸入存款金額:");//提示用戶存款金額
<br>                double  number = sc.nextDouble();//接收用戶輸入金額
<br>                @SuppressWarnings("unused")
<br>                Account account1 = new Account("asdd",number);//創(chuàng)建Accound對象
<br>                System.out.println("請您將鈔票疊整齊后放入存鈔口..");//提示用戶將鈔票放入存鈔口
<br>                try{
<br>                    Thread.sleep(2000);//模仿現(xiàn)實(shí)銀行存鈔,設(shè)置睡眠時(shí)間兩秒
<br>                    System.out.println("正在存鈔,請稍后...");//將輸出等待
<br>                    Thread.sleep(4000);//將其過程等待四秒
<br>                    System.out.println("存款成功!");//則輸出存款成功
<br>            System.out.println("您的存款金額為:"+number+"\t"+"當(dāng)前賬戶余額為:"+number);
<br>                                                               //輸出存款金額
<br>            System.out.println("1.是否打印憑條"+"\t"+"2.是否繼續(xù)存款"+"\t"+"3.退出");
<br>                                                             //用戶繼續(xù)選擇
<br>                    int sr1 = sc.nextInt();//接收用戶輸入
<br>                    switch(sr1){
<br>                    //接收用戶是否打印憑條
<br>                        case 1:
<br>                            System.out.println("dce");
<br>                            System.out.println("打印完成,退出打印");
<br>                            break;
<br>                        //接收用戶是否繼續(xù)存款
<br>                        case 2:
<br>                            System.out.println("繼續(xù)存款");
<br>                            banking();
<br>                            break;
<br>                        //接收用戶退出
<br>                        case 3:
<br>                            System.out.println("您已退出存款選項(xiàng)");
<br>                            break;
<br>                        //防止隨意輸入    
<br>                        default :
<br>                            System.out.println("您輸入有誤,請重新輸入");
<br>                            break;
<br>                    }
<br>                }catch(Exception e){
<br>                    e.printStackTrace();
<br>                }
<br>                break;
<br>            //輸入2則進(jìn)入取款功能    
<br>            case 2:System.out.println("請輸出您的取款金額:");
<br>                Account account2 = new Account("小明",7000);//創(chuàng)建Account對象并傳參數(shù)
<br>                DrawMoney ka = new DrawMoney(account2,3000);//創(chuàng)建銀行卡取款3000元
<br>                DrawMoney zhe = new DrawMoney(account2,4000);//創(chuàng)建存折取款2000元
<br>                new Thread(ka).start();//開啟銀行卡線程
<br>                new Thread(zhe).start();//開啟存折線程
<br>                break;
<br>            //輸入3則退出銀行系統(tǒng)
<br>            case 3:
<br>                System.out.println("您已安全退出中國銀行系統(tǒng),感謝您的光臨!");
<br>                System.exit(0);//退出指令
<br>                break;
<br>            //防止用戶隨意輸入則進(jìn)行友情提示
<br>            default :
<br>                System.out.println("您輸入有誤,請重新輸入");
<br>                break;
<br>        }
<br>    }
<br>    //繼承存款金額
<br>    public static void banking(){
<br>        @SuppressWarnings("resource")
<br>        Scanner sc2 = new Scanner(System.in);//接收控制臺鍵盤輸入
<br>                 System.out.println("請您輸入存款金額:");//提示用戶存款金額
<br>                double  number = sc2.nextDouble();//接收用戶輸入金額
<br>                @SuppressWarnings("unused")
<br>                Account account2 = new Account("asdd",number);//創(chuàng)建Accound對象
<br>                System.out.println("請您將鈔票疊整齊后放入存鈔口..");//提示用戶將鈔票放入存鈔口
<br>                try{
<br>                    Thread.sleep(2000);//模仿現(xiàn)實(shí)銀行存鈔,設(shè)置睡眠時(shí)間兩秒
<br>                    System.out.println("正在存鈔,請稍后...");//將輸出等待
<br>                    Thread.sleep(4000);//將其過程等待四秒
<br>                    System.out.println("存款成功!");//則輸出存款成功
<br>            System.out.println("您的存款金額為:"+number+"\t"+"當(dāng)前賬戶余額為:"+number);
<br>                                                         //輸出存款金額
<br>                    System.out.println("1.是否打印憑條"+"\t"+"\t"+"2.退出");//用戶繼續(xù)選擇
<br>                    int sr3 = sc2.nextInt();//接收用戶輸入
<br>                    switch(sr3){
<br>                    //接收用戶是否打印憑條
<br>                        case 1:
<br>                            System.out.println("dce");
<br>                            System.out.println("打印完成,退出打印");
<br>                            break;
<br>                        case 2:
<br>                            System.out.println("您已退出存款選項(xiàng)");
<br>                            break;
<br>                        //防止隨意輸入    
<br>                        default :
<br>                            System.out.println("您輸入有誤,請重新輸入");
<br>                            break;
<br>                    }
<br>                }catch(Exception e){
<br>                    e.printStackTrace();
<br>                }   
<br>    }
<br> }  
<br> ```
<br> ```
<br> package cn.chinabank;//中國銀行包
<br> //取錢類實(shí)現(xiàn)Runnable接口
<br> public class DrawMoney implements Runnable{
<br>    private Account account;//私有屬性-賬戶
<br>    private double money;//私有屬性-金額
<br>    //構(gòu)造方法并傳值
<br>    public DrawMoney(Account account,double money){
<br>        this.account = account;//當(dāng)前賬戶
<br>        this.money = money;//當(dāng)前金額
<br>    }
<br>    @Override
<br>    public void run() {
<br>        while(true){
<br>            //線程同步塊
<br>            //相當(dāng)于將account隊(duì)形鎖住,只有執(zhí)行完代碼塊,才可以釋放,其他線程才能拿到
<br>            //account必須是同一個(gè)
<br>            synchronized(account){
<br>                //如果賬戶金額大于等于金額
<br>                if(account.getNumber()&=money){
<br>                    account.setNumber(account.getNumber()-money);//賬戶余額減去取款金額
<br>                    //輸出取款成功,并顯示當(dāng)前賬戶剩余金額
<br>                    System.out.println("你已取款成功,您取款金額為:"+money+"元,剩余金額為:"+account.getNumber()+"元");
<br>                    //異常處理
<br>                    try{
<br>                    Thread.sleep(2000);//調(diào)用方法設(shè)置睡眠兩秒
<br>                    }catch(InterruptedException e){
<br>                        e.printStackTrace();
<br>                }
<br>                //否則提示取款不成功,顯示當(dāng)前賬戶余額
<br>                }else{
<br>                    System.out.println("對不起,您的賬戶余額不足,您當(dāng)前賬戶余額剩余"+account.getNumber()+"元");//輸出賬戶金額
<br>                }
<br>            }
<br>            break;
<br>        }   
<br>    }
<br> ```

12.多線程 - 生產(chǎn)者與消費(fèi)者應(yīng)用案例

多線程的開發(fā)中有一個(gè)最經(jīng)典的操作案例,就是生產(chǎn)者-消費(fèi)者
生產(chǎn)者不斷生產(chǎn)產(chǎn)品,消費(fèi)者不斷取走產(chǎn)品

/**
* 生產(chǎn)者與消費(fèi)者應(yīng)用案例
* sleep與wait區(qū)別
* sleep讓當(dāng)前的線程進(jìn)入休眠狀態(tài),讓出cpu,讓其他線程執(zhí)行
* 如果用同步的話,有對象鎖的時(shí)候,是不會釋放的,只能等待此線程使用完,才可以使用
* wait會釋放對象鎖,必須等待其他線程喚醒
* @author JEEP-711
*
*/
public class ScXf {
public static void main(String[] args) {
Phones p = new Phones(null, null);//創(chuàng)建Phones對象
PhoneSc s = new PhoneSc(p);//創(chuàng)建PhoneSc對象
PhoneXf x = new PhoneXf(p);//創(chuàng)建PhoneXf對象
new Thread(s).start();//啟動生產(chǎn)者線程
new Thread(x).start();//啟動消費(fèi)者線程
}
}
/**
* 手機(jī)生產(chǎn)者,單獨(dú)的生產(chǎn)者,實(shí)現(xiàn)Runnable接口
* @author JEEP-711
*
*/
class PhoneSc implements Runnable{
private Phones phones;
public PhoneSc(Phones phones){
this.phones = phones;
}
@Override
public void run() {
//不斷地生產(chǎn)20份,生產(chǎn)的過程
for (int i = 0; i < 50; i++) {
if(i%2==0){
phones.set("金立手機(jī)", "金立手機(jī),中國造!");
}else{
phones.set("小米手機(jī)", "小米手機(jī),為發(fā)燒而生!");
}
}
}
}
/**
* 手機(jī)消費(fèi)者,顧客
* @author JEEP-711
*
*/
class PhoneXf implements Runnable{
private Phones phones;
public PhoneXf(Phones phones){
this.phones = phones;
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
phones.get();//調(diào)用消費(fèi)產(chǎn)品方法
}
}
}
/**
* 產(chǎn)品的對象,生產(chǎn)的手機(jī)
* @author JEEP-711
*
*/
class Phones{
@Override
public String toString() {
return "Phones [name=" + name + ", content=" + content + "]";
}
private String name;
private String content;
/**true表示可以生產(chǎn),false表示可以消費(fèi)
* 作為標(biāo)記,如何flag等于true表示可以生產(chǎn),如何flag等于false表示不可生產(chǎn)
* 如果flag等于false表示可以消費(fèi)狀態(tài),可以取走,flag等于true表示不能取走
* 解決重復(fù)值得問題
*/
private boolean flag = true;//表示可以生產(chǎn),false表示可以消費(fèi)
//構(gòu)造方法
public Phones(String name, String content) {
super();
this.name = name;
this.content = content;
}
//取得名稱方法
public String getName() {
return name;
}
//設(shè)置名稱方法
public void setName(String name) {
this.name = name;
}
//取得內(nèi)容方法
public String getContent() {
return content;
}
//設(shè)置內(nèi)容方法
public void setContent(String content) {
this.content = content;
}
/**
* 通過同步,解決了取值錯(cuò)誤問題
* @param name
* @param content
*/
//生產(chǎn)制造同步方法
public synchronized void set(String name, String content){
if(!flag){
try {
//調(diào)用該方法,當(dāng)前線程進(jìn)入等待池等待狀態(tài),沒有指定時(shí)間,
//需要其他線程喚醒,釋放對象鎖,讓出cpu
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
flag = false;//表示可以消費(fèi),取走
this.notify();//喚醒在該監(jiān)視器上的一個(gè)線程
}
//消費(fèi)產(chǎn)品同步取值方法
public synchronized void get(){
if(flag){
try {
//調(diào)用該方法,當(dāng)前線程進(jìn)入等待池等待狀態(tài),沒有指定時(shí)間,
//需要其他線程喚醒,釋放對象鎖,讓出cpu
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+":"+this.getContent());
flag = true;
this.notify();
}
}

13.線程池

線程池是預(yù)先創(chuàng)建線程的一種技術(shù),線程池在還沒有任務(wù)到來之前,創(chuàng)建一定數(shù)量的線程,放入空閑隊(duì)列中,然后對這些資源進(jìn)行復(fù)用,減少頻繁的創(chuàng)建和銷毀對象
java里面線程池的頂級接口是Executor,是一個(gè)執(zhí)行線程的工具
線程池接口是ExecutorServise
1.java.util.concurrent包:并發(fā)編程中很常用的實(shí)用工具包
2.Executor接口:執(zhí)行已提交Runnable任務(wù)的對象

ExecutorService接口:
Executor提供了管理終止的方法,以及可能為跟蹤一個(gè)或
多個(gè)異步任務(wù)執(zhí)行狀況而發(fā)生的Future的方法
Executors類:此包中所定義的Executor、ExecutorService等的工廠和實(shí)現(xiàn)方法
在Executors類里提供了一些靜態(tài)工廠,生成一些常用的線程池
newSingleThreadExecutor:
創(chuàng)建一個(gè)單線程的線程池,這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程
串行執(zhí)行所有的任務(wù),如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會有一個(gè)新的線程來替代它,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行
newFixedThreadPool:
創(chuàng)建固定大小的線程池,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大線程池的大小一旦達(dá)到最大值就會保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會補(bǔ)充一個(gè)新線程
newCacheadThreadPool:
創(chuàng)建一個(gè)可緩存的線程池,如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會回收部分空閑(60秒不執(zhí)行)的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來處理任務(wù),此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說jvm)能夠創(chuàng)建的最大線程大小
newSchediledThreadPool:
創(chuàng)建一個(gè)大小無限制的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)需求

使用線程池的步驟:
- (1)調(diào)用Executors類的靜態(tài)工廠方法創(chuàng)建一個(gè)ExecutorService對象或ScheduledExecutorService對象,其中前者代表簡單的線程池,后者代表能以任務(wù)調(diào)度方式執(zhí)行線程的線程池
- (2)創(chuàng)建Runnable實(shí)現(xiàn)類或Callable實(shí)現(xiàn)類的實(shí)例,作為線程執(zhí)行任務(wù)
- (3)調(diào)用ExecutorService對象的submit方法來提交Runnable實(shí)例或Callable實(shí)例;或調(diào)用ScheduledExecutorService的schedule來執(zhí)行線程
- (4)當(dāng)不想提交任何任務(wù)時(shí)調(diào)用ExecutorService對象的shutdown方法來關(guān)閉線程池

初學(xué)(JAVA 多線程 高級階段) 難點(diǎn): ★★★★★

希望每一篇文章都能夠?qū)ψx者們提供幫助與提升,這乃是每一位筆者的初衷

感謝您的閱讀 歡迎您的留言與建議

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號