Java知識點總結之多執行緒基礎

2022-11-15 18:01:23
本篇文章給大家帶來了關於的相關知識,其中主要介紹了關於多執行緒的相關內容,同一時刻,可以執行多個執行緒。例如:使用迅雷可以同時下載多個檔案,下面一起來看一下,希望對大家有幫助。

程式設計師必備介面測試偵錯工具:

推薦學習:《》

一、執行緒相關概念

1、程式:是為完成特定任務、用某種語言編寫的一組指令的集合。簡單的說:就是我們寫的程式碼

2、程序:是指執行中的程式,比如我們使用QQ,就啟動了一個程序,作業系統就會為 該程序分配記憶體空間。當我們使用迅雷,又啟動了一個程序,作業系統將為迅雷分配新的記憶體空間。

程序是程式的一次執行過程,或是正在執行的一個程式。是動態過程:有它自身的產生、存在和消亡的過程。

3、執行緒:是程序建立的,是程序的實體,一個程序可以有多個執行緒,例如使用迅雷下載檔案,迅雷相當於程序,下載的檔案相當於執行緒。

4、單執行緒:同一時刻,只允許執行一個執行緒

5、多執行緒:同一時刻,可以執行多個執行緒。例如:使用迅雷可以同時下載多個檔案。

6、並行:同一時刻,多個任務交替進行。單核CPU實現的多工就是並行。

7、並行:同一時刻,多個任務同時進行。多核CPU可以實現並行,當任務較多時,並行和並行有可能同時發生。

二、執行緒基本使用

建立執行緒有兩種方法:

1、繼承Thread類,重寫run方法。

2、實現Runnable介面,重寫run方法。

注意:Thread類實現了Runnable介面。

(一) 繼承Thread類,重寫run方法

public class Thread01 {
    public static void main(String[] args) throws InterruptedException {
        Cat cat = new Cat();
        cat.start();
        System.out.println("主執行緒" + Thread.currentThread().getName());
        for (int i = 1; i <= 5; i++) {
            System.out.println("主執行緒i=" + i);
            Thread.sleep(1000);
        }
    }}class Cat extends Thread{
    int times = 0;
    @Override    public void run() {
        while (true) {
            System.out.println("你好" + (++times) + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (times == 5){
                break;
            }
        }
    }}
登入後複製

1、在繼承Thread類,重寫了run()方法後,在main方法中需要建立物件,並呼叫start()方法,啟動執行緒。

2、使用start()方法,會呼叫重寫的run()方法。

3、如果main方法中,start()方法後面還有執行語句,並且run()方法中也有執行語句,main執行緒中會啟動一個子執行緒Thread-0,主執行緒不會阻塞,主執行緒與子執行緒會交替執行。

注意:如果主執行緒執行完畢,但子執行緒未執行完,程序不會結束,所有執行緒執行完畢後,程序自動結束。

4、在主執行緒中為什麼使用start()方法去呼叫run()方法,而不是直接呼叫run()方法,因為run()方法是一個普通方法,沒有真正啟動執行緒,如果呼叫run()方法,就會將run()方法執行完畢後,再執行main方法剩餘語句,主執行緒就會阻塞。

所以上面程式都運算結果是:

主執行緒main
主執行緒i=1你好1Thread-0主執行緒i=2你好2Thread-0主執行緒i=3你好3Thread-0主執行緒i=4你好4Thread-0主執行緒i=5你好5Thread-0
登入後複製

(二) 實現Runnable介面,重寫run方法

public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        thread.start();
    }}class Dog implements Runnable{
    @Override    public void run() {
        int count = 0;
        while (true) {
            System.out.println("小狗汪汪叫" + (++count) + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10){
                break;
            }
        }
    }}
登入後複製

1、因為Runnable介面中沒有start()方法,所以需要代理

2、先在main方法中需要建立物件,然後建立一個Thread物件,將之前建立的物件傳入Thread,藉助Thread,呼叫start()方法,建立執行緒。

3、建議使用Runnable,因為實現Runnable介面更加適合多個執行緒共用一個資源的情況,避免了單繼承的限制。

三、執行緒終止

(一)基本說明

1、當執行緒執行完任務時會自動退出。

2、也可以使用變數控制run()方法退出終止執行緒。

(二)操作

1、如果一個執行緒中的run()方法內部是一個while(true){},即一個無限迴圈

2、我們可以線上程內部,建立一個boolean屬性loop,讓loop = true,然後while(loop){}

3、再提供一個setLoop方法,這樣就可以在其他類中呼叫setLoop方法,改變boolean值,來控制執行緒終止。

(三)程式碼演示

public class ThreadExit {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        Thread.sleep(10000);
        t.setLoop(false);
    }}class T extends Thread{
    private boolean loop = true;
    private int times = 0;
    
    @Override    public void run() {
        while (loop){
            System.out.println("hello" + (++times));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }}
登入後複製

四、執行緒常用方法

(一)第一組

1、setName:設定執行緒名稱,使之與引數name相同。

2、getName:返回該執行緒名稱。

3、start:使該執行緒開始執行,JAVA虛擬機器器底層呼叫該執行緒的start0方法。

4、run:呼叫執行緒物件的run方法。

5、setPriority:更改執行緒的優先順序。

6、getPriority:獲取執行緒的優先順序。

7、sleep:在指定的毫秒數內,讓當前正在執行的執行緒休眠。

8、interrupt:中斷執行緒。

注意事項:

1、start()底層會建立新的執行緒,呼叫run(),run()就是一個簡單的方法呼叫,不會啟動新執行緒

2、中斷執行緒一般用於中斷正在休眠的執行緒,並沒有真正的結束執行緒,所以如果執行緒中每輸出內容後,會呼叫Thread.sleep()進行休眠的話,我們可以呼叫interrupt()方法將其提前喚醒

程式碼演示:

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("邱崇源");
        t.setPriority(1);
        t.start();
        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("hi" + i);
        }
        System.out.println(t.getName() + "優先順序是" + t.getPriority());
        t.interrupt();
    }}class T extends Thread{
    @Override    public void run() {
        while (true){
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName() + "正在吃包子" + i);
            }
            try {
                System.out.println(Thread.currentThread().getName() + "休眠中");
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + "被喚醒了");
            }
        }
    }}
登入後複製

(二)第二組

1、yield:是一個靜態方法,表示執行緒的禮讓。讓出CPU,讓其他執行緒執行,但是不一定成功,因為這取決於CPU,如果CPU認為兩個執行緒可以一起執行,則不進行禮讓,所以如果CPU的核數多,並且執行緒數少,禮讓就會大概率失敗

2、join:表示執行緒的插隊,假如主執行緒與子執行緒正在交替執行,我們想先讓子執行緒執行完畢,然後再讓主執行緒執行,就可以使用執行緒插隊,在主執行緒中,建立子執行緒物件,並呼叫join方法,可以實現執行緒插隊,執行緒插隊一定會成功,先執行完插入執行緒任務後,再繼續執行主執行緒

程式碼演示:

public class ThreadMethod02 {
    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        a.start();
        for (int i = 1; i <= 20; i++) {
            System.out.println("主執行緒(小弟)吃了" + i + "個包子");
            Thread.sleep(1000);
            if (i == 5) {
                System.out.println("主執行緒(小弟)讓子執行緒(大哥)先吃");
                a.join();
                //Thread.yield();
                System.out.println("子執行緒(大哥)吃完了,主執行緒(小弟)接著吃");
            }
        }
    }}class A extends Thread {
    @Override    public void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println("子執行緒(大哥)吃了" + i + "個包子");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}
登入後複製

五、使用者執行緒和守護執行緒

1、使用者執行緒:也叫工作執行緒,執行緒的任務執行完或通知方式結束。

2、守護執行緒:一般是為工作執行緒服務的,當所有的使用者執行緒結束守護執行緒自動結束

應用場景:

如果有兩個執行緒,主執行緒執行結束,但子執行緒是無限迴圈。我們想讓主執行緒結束的同時,子執行緒也結束,就需要讓子執行緒變成守護執行緒。

在主執行緒中,建立子執行緒物件,並呼叫setDaemon(true),讓子執行緒變成守護執行緒。

注意:一定要放在start方法之前

程式碼演示:

public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();
        for (int i = 1; i <= 10; i++) {
            System.out.println("寶強正在辛苦工作...");
            Thread.sleep(1000);
        }
        System.out.println("寶強回家了,宋喆倉惶跑路...");
    }}class MyDaemonThread extends Thread{
    @Override    public void run() {
        while (true){
            System.out.println("馬蓉與宋喆正在爽歪歪...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}
登入後複製

六、執行緒的生命週期

1、JDK 中用 Thread.State 列舉表示了執行緒的幾種狀態

在這裡插入圖片描述

2、執行緒狀態轉換圖

在這裡插入圖片描述

七、執行緒的同步

1、應用場景:

假如有100張票,有三個執行緒同時進入該方法買票,票就有可能超賣。所以我們需要執行緒同步機制,保證資料在同一時刻,最多有一個執行緒存取。

可以採取同步方法,在方法中加入synchronized關鍵字。

也可以採用同步程式碼塊,synchronized(物件){}

注意:synchronized是非公平鎖,如果這次第一個執行緒存取了資料,那麼下一次第一個執行緒也有可能存取到資料。

如果同步方法是非靜態的,那麼鎖可以是this,也可以是其他物件,但要求是同一個物件。

例:synchronized(this)

如果同步方法是靜態的,鎖為當前類本身。

例:synchronized(類名:class)

2、程式碼演示:

public class SellTicket {
    public static void main(String[] args) {
        SellTicket02 sellTicket04 = new SellTicket02();
        Thread thread1 = new Thread(sellTicket04);
        Thread thread2 = new Thread(sellTicket04);
        Thread thread3 = new Thread(sellTicket04);
        thread1.start();
        thread2.start();
        thread3.start();
    }}class SellTicket02 implements Runnable {
    public static int ticket = 100;
    private boolean loop = true;

    public synchronized void sell() {
        if (ticket <= 0) {
            loop = false;
            return;
        }
        System.out.println("賣出了一張票,還剩" + (--ticket) + "張");
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override    public void run() {
        while (loop) {
            sell();
        }
    }}
登入後複製

八、執行緒的死鎖

1、基本介紹

多個執行緒都佔用了對方的鎖資源,但不肯相讓,就導致了死鎖,在程式設計中,一定要避免死鎖的發生。

2、發生場景:

例如:A和B的面前都各有兩道門,A的第一道門是o1,第二道門是o2。B的第一道門是o2,第二道門是o1。他們的面前有兩把鎖,一個是o1鎖,一個是o2鎖,假如A搶到了o1鎖,B搶到了o2鎖,但是他們只可開啟各自的第一道門,第二道門都沒有開啟,那麼他們都無法釋放自己的鎖資源,又不可能相讓,因此發生了死鎖。

3、程式碼演示:

public class DeadLock_ {
	public static void main(String[] args) { //模擬死鎖現象 
		DeadLockDemo A = new DeadLockDemo(true); 
		A.setName("A 執行緒"); 
		DeadLockDemo B = new DeadLockDemo(false); 
		B.setName("B 執行緒");
		A.start(); 
		B.start(); 
	} }class DeadLockDemo extends Thread { 
	static Object o1 = new Object();// 保證多執行緒,共用一個物件,這裡使用 static 
	static Object o2 = new Object(); 
	boolean flag; 
	
	public DeadLockDemo(boolean flag) {//構造器 
		this.flag = flag; 
	}
	
	@Override 
	public void run() {  
		if (flag) { 
			synchronized (o1) { 
				System.out.println(Thread.currentThread().getName() + " 進入 1"); 
				synchronized (o2) { 
					System.out.println(Thread.currentThread().getName() + " 進入 2"); 
				} 
			} 
		} else {
			synchronized (o2) { 
				System.out.println(Thread.currentThread().getName() + " 進入 3"); 
				synchronized (o1) {  
					System.out.println(Thread.currentThread().getName() + " 進入 4"); 
				} 
			} 
		} 
	} }
登入後複製

九、釋放鎖

1、下面操作會釋放鎖

當前執行緒的同步方法,同步程式碼塊執行結束。
當前執行緒在同步程式碼塊,同步方法中遇到break,return
當前執行緒在同步程式碼塊,同步方法中出現了未處理的錯誤或異常,導致異常結束。
當前執行緒在同步程式碼塊,同步方法中執行的執行緒物件的wait方法,當前執行緒暫停,並釋放鎖。

2、 下面操作不會釋放鎖

執行緒執行同步程式碼塊和同步方法時,程式呼叫Thread.sleep(),Thread.yield()方法暫停當前執行緒的執行,不會釋放鎖。
執行緒執行同步程式碼塊時,其他執行緒呼叫了該執行緒的suspend()方法將該執行緒掛起,該執行緒不會釋放鎖。

推薦學習:《》

以上就是Java知識點總結之多執行緒基礎的詳細內容,更多請關注TW511.COM其它相關文章!