詳細瞭解java多執行緒機制

2022-06-17 14:01:32
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於多執行緒的相關問題,包括了什麼是程式、程序、執行緒、建立執行緒的三種方式、執行緒的狀態等等內容,下面一起來看一下,希望對大家有幫助。

推薦學習:《》

一、程式、程序、執行緒

1.1 什麼是程式

程式(program):是為完成特定任務、用某種語言編寫的一組指令的集合,是一段靜態的程式碼。 (程式是靜態的)

在這裡插入圖片描述

1.2 什麼是程序

程序(process):是程式的一次執行過程,正在執行的一個程式,程序作為資源分配的單位,在記憶體中會為每個程序分配不同的記憶體區域。 (程序是動態的)是一個動的過程 ,程序的生命週期 : 有它自身的產生、存在和消亡的過程

在這裡插入圖片描述
目前作業系統都是支援多程序,可以同時執行多個程序,通過程序ID區分
在這裡插入圖片描述

1.3 什麼是執行緒

執行緒(thread):程序中的一條執行路徑,也是CUP的基本排程單位,一個程序由一個或多個執行緒組成,彼此間完成不同的工作,多個執行緒同時執行,稱為多執行緒。

在這裡插入圖片描述
在這裡插入圖片描述
執行緒的組成

任何一個執行緒都具有的基本組成部分:

  • CPU時間片:作業系統(OS)會為每一個執行緒分配執行時間。
  • 執行資料:堆空間(儲存執行緒需要使用的物件,多個執行緒可以共用堆中的物件);棧空間(儲存執行緒需要使用的區域性變數,每個執行緒都擁有獨立的棧)

執行緒的特點

  • 執行緒搶佔式執行(效率高、可防止單一執行緒長時間獨佔CPU)
  • 單核CPU在執行的時候,是按照時間片執行的,一個時間片只能執行一個執行緒,因為時間片特別的短,我們感受到的就是「同時」進行的。
  • 多核CPU真正意義上做到了一個時間片多個執行緒同時進行
  • 在單核CPU中,宏觀上同時進行,微觀上順序執行

1.4 程序和執行緒的區別

  • 程序是作業系統中資源分配的基本單位,而執行緒是CPU的基本排程單位
  • 一個程式執行後至少有一個程序
  • 一個程序可以包含多個執行緒,但是至少需要有一個執行緒,否則這個程序是沒有意義的
  • 程序間不能共用資料段地址,但通程序的執行緒之間可以。

二、建立執行緒的三種方式

2.1 繼承Thread類重寫run()方法

具體實現

1.繼承Thread類
2.重寫run()方法
3.建立子類物件
4.呼叫start()方法(PS:不要呼叫run()方法,這樣相當於普通呼叫物件的方法,併為啟動執行緒

繼承類

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            System.out.println("子執行緒:==>" + i);
        }
    }}

測試類

public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 1; i <= 50; i++) {
            System.out.println("主執行緒:-->"+i);
        }
    }}

結果
在這裡插入圖片描述

獲取執行緒ID和名稱

getId()//獲取執行緒的id,每個執行緒都有自己的id
getName()//獲取執行緒名字
Thread.currentThread()//獲取當前執行緒

程式碼

public class TestThread {

	public static void main(String[] args) {
		MyThread t=new MyThread();
		t.start();
        //只能在繼承Thread類的情況下用
		System.out.println("執行緒id:"+t.getId());
		System.out.println("執行緒名字:"+t.getName());
        //呼叫Thread類的靜態方法獲取當前執行緒(這裡獲取的是主執行緒)
		System.out.println("執行緒id:"+Thread.currentThread().getId());
		System.out.println("執行緒名字:"+Thread.currentThread().getName());
	}}

在這裡插入圖片描述

修改執行緒名稱

只能修改執行緒的名稱,不能修改執行緒的id(id是由系統自動分配)
1、使用執行緒子類的構造方法賦值
2、呼叫執行緒物件的setName()方法

程式碼

public class MyThread extends Thread{
	public MyThread() {}//無參構造器
	public MyThread(String name) {
		super(name);
	}
	public void run() {
		for(int i=1;i<=50;i++) {}
	}}
public class TestThread {

	public static void main(String[] args) {
		MyThread t1=new MyThread("子執行緒1");//通過構造方法
		MyThread t2=new MyThread();
		t2.setName("子執行緒2");
		System.out.println("執行緒t1的id:"+t1.getId()+" 名稱:"+t1.getName());
		System.out.println("執行緒t2的id:"+t2.getId()+" 名稱:"+t2.getName());
	}}

在這裡插入圖片描述

2.2 實現Runnable介面實現run()方法

具體實現

1.實現Runnable介面
2.實現run()方法
3.建立實現類物件
4.建立執行緒類物件
5.呼叫start()方法

實現介面

public class MyRunnable implements Runnable{//實現介面
	@Override
	public void run() {//實現run方法
		// TODO Auto-generated method stub
		for(int i=1;i<=10;i++) {
			System.out.println("子執行緒:"+i);
		}
	}}

測試類

public class TestRunnable {
	public static void main(String[] args) {
		//1.建立MyRunnable物件,表示執行緒執行的功能
		Runnable runnable=new MyRunnable();
		//2.建立執行緒物件
		Thread th=new Thread(runnable);
		//3.啟動執行緒
		th.start();
		for(int i=1;i<=10;i++) {
			System.out.println("主執行緒:"+i);
		}		
	}}

在這裡插入圖片描述

使用匿名內部類

如果一個執行緒方法我們只使用一次,那麼就不必設定一個單獨的類,就可以使用匿名內部類實現該功能

public class TestRunnable {
	public static void main(String[] args) {
		Runnable runnable=new Runnable() {			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int i=1;i<=10;i++) {
					System.out.println("子執行緒:"+ i);
				}
			}
		};
		
		Thread th=new Thread(runnable);
		th.start();
	}}

2.3 實現Callable介面

Callable和Thread、Runnable比較

對比繼承Thread類和實現Runnable介面建立執行緒的方式發現,都需要有一個run()方法,但是這個run()方法有不足:

  • 沒有返回值
  • 不能丟擲異常

基於上面的兩個不足,在JDK1.5以後出現了第三種建立執行緒的方式:實現Callable介面

實現Callable介面的好處:

  • 有返回值
  • 能丟擲異常

缺點:

  • 建立執行緒比較麻煩

1.實現Callable介面,可以不帶泛型,如果不帶泛型,那麼call方法的返回值就是Object型別

2.如果帶泛型,那麼call的返回值就是泛型對應的型別

3.從call方法看到:方法有返回值,可以丟擲異常

具體實現

實現介面

import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class TestCallable implements Callable<Integer>{

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		return new Random().nextInt(10);
	}}

測試類

import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		TestCallable tc=new TestCallable();
		FutureTask<Integer> ft=new FutureTask<>(tc);
		//建立執行緒物件
		Thread th=new Thread(ft);
		th.start();
		//獲取執行緒得到的返回值
		Integer In=ft.get();
		System.out.println(In);
	}}

三、執行緒的狀態

3.1 基本四狀態

在這裡插入圖片描述

3.2 等待狀態

在這裡插入圖片描述

3.3 阻塞狀態

在這裡插入圖片描述

四、執行緒常用的方法

  • 休眠(當前執行緒主動休眠millis毫秒)public static void sleep(long millis)

  • 放棄(當前執行緒主動放棄時間片,回到就緒狀態,競爭下一次時間片)public static void yield()

  • 加入(當一個執行緒呼叫了join方法,這個執行緒就會先被執行,它執行結束以後才可以去執行其餘的執行緒)public final void join()//必須先start(),在join(),才有效

  • 優先順序(執行緒優先順序為1–10,預設為5,優先順序越高,表示獲取CPU機會越多)執行緒物件.setPriority()

  • 守護執行緒

    • 執行緒物件.setDaemon(true);設定為守護執行緒
    • 執行緒有兩類:使用者執行緒(前臺執行緒)、守護執行緒(後臺執行緒)
    • 如果程式中所有前臺執行緒都執行完畢了,後臺執行緒也會自動結束
    • 垃圾回收器執行緒屬於守護執行緒

4.1 執行緒休眠(sleep)

public static void sleep(long millis)當前執行緒主動休眠millis毫秒

子執行緒

public class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}

PS:sleep()的異常在run方法中是不能丟擲的,只能用try–catch處理
測試類

public class Test01 {
    public static void main(String[] args) {
        SleepThread sleepThread = new SleepThread();
        sleepThread.start();
    }}

結果:每次間隔100ms輸出一次
在這裡插入圖片描述

4.2 執行緒放棄(yield)

public static void yield()當前執行緒主動放棄時間片,回到就緒狀態,競爭下一次時間片

子執行緒

public class YieldThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            Thread.yield();//主動放棄資源
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) {
        YieldThread yieldThread01 = new YieldThread();
        YieldThread yieldThread02 = new YieldThread();
        yieldThread01.start();
        yieldThread02.start();
    }}

結果:基本都會交替進行,也會有一個執行緒連輸出
在這裡插入圖片描述

4.3 執行緒加入(join)

當一個執行緒呼叫了join方法,這個執行緒就會先被執行,它執行結束以後才可以去執行其餘的執行緒,必須先start,再join才有效

子執行緒

public class JoinThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if(i==5){
                JoinThread joinThread = new JoinThread();
                joinThread.start();
                joinThread.join();
            }
        }
    }}

結果:當主執行緒列印到5的時候,這時候子執行緒加入進來,就先執行完子執行緒,在執行主執行緒
在這裡插入圖片描述

4.4 守護執行緒(setDaemon)

將子執行緒設定為主執行緒的伴隨執行緒,主執行緒停止的時候,子執行緒也不要繼續執行了
注意:先設定,在啟動

子執行緒

public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=1000;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread daemonThread = new TestThread();
        daemonThread.setDaemon(true);//設定守護執行緒
        daemonThread.start();
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            Thread.sleep(100);
        }
    }}

結果:當主執行緒結束時,子執行緒也跟著結束,並不會繼續執行下去列印輸出
在這裡插入圖片描述

4.5 執行緒優先順序(setPriority)

執行緒優先順序為1-10,預設為5,優先順序越高,表示獲取CPU機會越多
在這裡插入圖片描述

子執行緒

public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }}

測試

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread th1 = new TestThread();
        TestThread th2 = new TestThread();
        TestThread th3 = new TestThread();
        th1.setPriority(10);//設定執行緒1優先順序10
        th1.start();
        th2.start();//執行緒2優先順序預設不變,為5
        th3.setPriority(1);//設定執行緒3優先順序為1
        th3.start();
    }}

結果:優先順序(th1>th2>th3)執行緒3應該在最後列印
在這裡插入圖片描述

五、執行緒安全問題

5.1 賣票案例

需求:模擬三個視窗,每個視窗有100個人,同時搶10張票
在這裡插入圖片描述
使用繼承Runnable介面的方法

public class BuyTicketRunnable implements Runnable{
	
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<=100;i++) {
			if(ticketNum<=0) break;
			System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
			ticketNum--;
		}
	}}

測試方法

public class BuyTicketTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable runnable=new BuyTicketRunnable();
		
		Thread th1=new Thread(runnable,"視窗1");
		Thread th2=new Thread(runnable,"視窗2");
		Thread th3=new Thread(runnable,"視窗3");
		
		th1.start();
		th2.start();
		th3.start();
	}}

結果
在這裡插入圖片描述

我們發現,不同視窗會搶到同一張票!!!,這在實際情況是不允許的,這是因為多個執行緒,在爭搶資源的過程中,導致共用的資源出現問題。一個執行緒還沒執行完,另一個執行緒就參與進來了,開始爭搶。(但視窗2搶到第10張票,還沒來得及ticketNum--操作,時間片就用完了,隨後被視窗三搶到CPU資源,此時的票數還是10,視窗三也搶到第十張票,也還沒來得及ticketNum--操作視窗三時間片由完了,視窗一搶到CPU資源,還是買到了第10張票)

多執行緒安全問題:

  • 當多執行緒並行存取臨界資源時,如果破壞原子操作,可能會造成資料不一致
  • 臨界資源:共用資源(同一物件),一次只能允許一個執行緒使用,才可以保證其正確性
  • 原子操作:不可分割的多步操作,被視作一個整體,其順序和步驟不可被打亂或預設

5.2 同步程式碼塊

synchronized(同步監視器)

  • 必須是參照資料型別,不能是基本資料型別
  • 也可以建立一個專門的同步監視器,沒有任何業務含義 (new Object)
  • 一般使用共用資源做同步監視器即可
  • 在同步程式碼塊中不能改變同步監視器物件的參照
  • 儘量不要String和包裝類Integer做同步監視器,建議使用final修飾同步監視器

對賣票案例改進

public class BuyTicketRunnable implements Runnable{
	static Object obj=new Object();
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
            //把具有安全隱患的程式碼鎖住即可,如果鎖多了就會效率低 
			synchronized (obj) {//鎖必須多個執行緒用的是同一把鎖!!也可以使用this,表示的是該物件本身
				System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
				ticketNum--;	
			}
		}
	}}
  • 多個程式碼塊使用了同一個同步監視器(鎖),鎖住一個程式碼塊的同時,也鎖住所有使用該鎖的所有程式碼塊,其他執行緒無法存取其中的任何一個程式碼塊
  • 多個程式碼塊使用了同一個同步監視器(鎖),鎖住一個程式碼塊的同時,也鎖住所有使用該鎖的所有程式碼塊, 但是沒有鎖住使用其他同步監視器的程式碼塊,其他執行緒有機會存取其他同步監視器的程式碼塊

5.3 同步方法

synchronized(同步方法)

  • 不要將run()定義為同步方法
  • 非靜態同步方法的同步監視器是this;靜態同步方法(static)的同步監視器是 類名.class 位元組碼資訊物件
  • 同步程式碼塊的效率要高於同步方法(原因:同步方法是將執行緒擋在了方法的外部,而同步程式碼塊鎖將執行緒擋在了程式碼塊的外部,但是卻是方法的內部)
  • 同步方法的鎖是this,一旦鎖住一個方法,就鎖住了所有的同步方法;同步程式碼塊只是鎖住使用該同步監視器的程式碼塊,而沒有鎖住使用其他監視器的程式碼塊

買票案例改進

public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
			BuyTicket();
		}
	}
	public synchronized void BuyTicket() {//鎖住的是:this,如果是靜態方法:當前類.class
		if(ticketNum>0) {
			System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
			ticketNum--;	
		}
	}}

5.4 Lock鎖

Lock鎖:

  • DK1.5後新增新一代的執行緒同步方式:Lock鎖,與採用synchronized相比,lock可提供多種鎖方案,更靈活
  • synchronized是Java中的關鍵字,這個關鍵字的識別是靠JVM來識別完成的呀。是虛擬機器器級別的。
    但是Lock鎖是API級別的,提供了相應的介面和對應的實現類,這個方式更靈活,表現出來的效能優於之前的方式。

對買票案例改進

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	Lock lock=new ReentrantLock();//介面=實現類  可以使用不同的實現類
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
			lock.lock();//開啟鎖
			try {
				if(ticketNum>0) {
					System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
					ticketNum--;	
				}
			}catch(Exception e) {
				e.printStackTrace();
			}finally {
				 //關閉鎖:--->即使有異常,這個鎖也可以得到釋放
				lock.unlock();
			}
		}
	}}

Lock和synchronized的區別

  • Lock是顯式鎖(手動開啟和關閉鎖,別忘記關閉鎖),synchronized是隱式鎖
  • Lock只有程式碼塊鎖,synchronized有程式碼塊鎖和方法鎖
  • 使用Lock鎖,JVM將花費較少的時間來排程執行緒,效能更好。並且具有更好的擴充套件性(提供更多的子類)

5.5 執行緒死鎖

  • 不同的執行緒分別佔用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了執行緒的死鎖
  • 出現死鎖後,不會出現異常,不會出現提示,只是所有的執行緒都處於阻塞狀態,無法繼續

*案例:男孩女孩一起去吃飯,但是桌子上只有兩根筷子,如果兩個人同時搶到一根筷子而不放棄,這樣兩個人都吃不上飯,這樣就形成死鎖了;必須要有一個人放棄爭搶,等待另一個人用完,釋放資源,這個人之後才會獲得兩根筷子,兩個人才能都吃上飯 *

package 多執行緒;class Eat{
    //代表兩個筷子
	public static Object o1=new Object();
	public static Object o2=new Object();
	public static void eat() {
		System.out.println("可以吃飯了");
	}}class BoyThread extends Thread{
	public void run() {
		synchronized (Eat.o1) {
			System.out.println("男孩拿到了第一根筷子!");
			synchronized (Eat.o2) {
				System.out.println("男孩拿到了第二根筷子!");
				Eat.eat();
			}
		}
	}}class GirlThread extends Thread{
	public void run() {
		synchronized (Eat.o2) {
			System.out.println("女孩拿到了第二根筷子!");
			synchronized (Eat.o1) {
				System.out.println("女孩拿到了第一根筷子!");
				Eat.eat();
			}
		}
	}}public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		girl.start();
	}}

結果
在這裡插入圖片描述
解決辦法

先讓男孩拿到筷子,執行緒休眠一下,等待男孩用完筷子,在啟動女孩執行緒

public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		girl.start();
	}}

在這裡插入圖片描述
在寫程式中要避免這種死鎖:減少同步資源的定義,避免巢狀同步

六、執行緒通訊問題

在Java物件中,有兩種池

  • 鎖池(synchronized
  • 等待池(wait();notify();notifyAll()

如果一個執行緒呼叫了某個物件的wait方法,那麼該執行緒進入到該物件的等待池中(並且已經將鎖釋放);
如果未來的某個時刻,另外一個執行緒呼叫了相同的物件notify方法或者notifyAll方法,那麼該等待池中的執行緒就會被喚醒,然後進入到物件的鎖池裡面去獲得該物件的鎖;
如果獲得鎖成功後,那麼該執行緒就會沿著wait方法之後的路徑繼續執行。注意:沿著wait方法之後執行

6.1 wait()和wait(long timeout)

  • wait():的作用是讓當前執行緒進入等待狀態,同時,wait()也會讓當前執行緒釋放它所持有的鎖。直到其他執行緒呼叫此物件的notify() 方法或 notifyAll() 方法,當前執行緒被喚醒(進入就緒狀態)
  • wait(long timeout):讓當前執行緒處於「等待(阻塞)狀態,直到其他執行緒呼叫此物件的notify()方法或 notifyAll() 方法,或者超過指定的時間量」,當前執行緒被喚醒(進入就緒狀態)

sleep和wait的區別:sleep進入阻塞狀態沒有釋放鎖,wait進入阻塞狀態但是同時釋放了鎖

6.2 notify()和notifyAll()

notify()和notifyAll()的作用,則是喚醒當前物件上的等待執行緒

  • notify()是喚醒單個執行緒
  • notifyAll()是喚醒所有的執行緒

6.3 生產者和消費者問題

案例:
假設倉庫中只能存放一件產品,生產者將生產出來的產品放入倉庫,消費者將倉庫中產品取走消費。
如果倉庫中沒有產品,則生產者將產品放入倉庫,否則停止生產並等待,直到倉庫中的產品被消費者取走為止。
如果倉庫中放有產品,則消費者可以將產品取走消費,否則停止消費並等待,直到倉庫中再次放入產品為止。
在這裡插入圖片描述

功能分解一:商品類

public class Product {//商品類
    private String name;//名字
    private String brand;//品牌
    boolean flag = false;//設定標記,false表示商品沒有,等待生產者生產

    public synchronized void setProduct(String name, String brand) {//生產商品,同步方法,鎖住的是this
        if (flag == true) {//如果flag為true,代表有商品,不生產,等待消費者消費
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生產商品
        this.setName(name);
        this.setBrand(brand);

        System.out.println("生產者生產了" +this.getBrand() +this.getName());
        //生產完,設定標誌
        flag = true;
        //喚醒消費執行緒
        notify();
    }

    public synchronized void getProduct() {
        if (flag == false) {//如果是false,則沒有商品,等待生產者生產
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有商品,消費
        System.out.println("消費者消費了" + this.getBrand() +this.getName());
        //設定標誌
        flag = false;
        //喚醒執行緒
        notify();
    }


    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }}

功能分解二:生產者執行緒

public class ProducterThread extends Thread {//生產者執行緒
    private Product p;

    public ProducterThread(Product p) {
        this.p = p;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            if(i%2==0){//如果是奇數,就生產巧克力,如果是偶數,就生產方便麵
                p.setProduct("巧克力","德芙");
            }else{
                p.setProduct("方便麵","康師傅");
            }
        }
    }}

功能分解三:消費者執行緒

public class CustomerThread extends Thread {//消費者執行緒
    private Product pro;

    public CustomerThread(Product pro) {
        this.pro = pro;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            pro.getProduct();
        }
    }}

功能分解四:測試類

public class Test {
    public static void main(String[] args) {
        Product p = new Product();
        ProducterThread pth = new ProducterThread(p);
        CustomerThread cth = new CustomerThread(p);
        pth.start();
        cth.start();
    }}

結果:生產者生產一件商品,消費者消費一件商品,交替進行

在這裡插入圖片描述

推薦學習:《》

以上就是詳細瞭解java多執行緒機制的詳細內容,更多請關注TW511.COM其它相關文章!