【線程】 併發 、並行、進程、線程

併發:指兩個或多個事件在同一時間段內發生。

並行:指兩個或多個事件在同一時刻發生。

 

進程:指一個內存中運行的應用程序,每個進程都有獨立的內存空間,一個應用程序可以運行多個進程。

線程:線程是進程中的一個執行單元。

 

小結:

線程的作用是提供一個輕量級執行單元——雖比進程小,但仍能執行任何java代碼。一般情況下,對操作系統來說,一個線程是一個完成的執行單元,但仍屬於一個進程,進程的地址空間在組成該進程的所有線程之間共享。也就是說,每個線程都可以獨立調度,而且有自己的棧和程序計數器,但會和同個進程中的其他線程共享內存和對象。

 

線程調度

  • 分時調度:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間
  • 搶佔式調度:優先級高的線程使用CPU,如果線程的優先級相同,則隨機選擇一個(線程隨機性),java的使用爲搶佔式調度

 

創建線程

創建多線程程序的第一種方式:

創建Thread類的子類
java.lang.Thread類:是描述線程的類,我們想要實現多線程程序,就必須繼承Thread類

實現步驟:
1.創建一個Thread類的子類
2.在Thread類中的子類重寫run方法
3.創建Thread類的子類對象
4.調用Thread類中的方法start,開啓線程,執行run方法
(多次啓動一個線程是非法的,特別是當線程已經結束後,不能再重新啓動)
(java的線程使用爲搶佔式調度)

package demo1;

public class MyThread01 extends Thread{
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("thread:"+i);
		}
	}
}
package demo1;

public class myDemo12 {
	public static void main(String[] args) throws Exception {
		MyThread01 myThread = new MyThread01();
		myThread.start();  //開闢新的棧空間,執行run方法
		
		for(int i=5;i<10;i++) {
			System.out.println("main:"+i);
		}
	}
}

運行結果:

main:5
thread:0
main:6
thread:1
main:7
thread:2
main:8
thread:3
main:9
thread:4

或者是:

使用匿名內部類

Thread thread = new Thread() {
			public void run() {
				for(int i=5;i<10;i++) {
					System.out.println("main:"+i);
				}
			}
		};
		
thread.start();

 

Thread常用方法

setName(Sting name);  //設置線程的名字

getName();  //獲取線程的名字

sleep(long ms);   //暫定執行線程

join();     //讓此線程強制執行,其他線程無法運行,必須等待此線程完成之後纔可以繼續執行

interrupt();    //當一個線程運行時,另外一個線程可以直接通過interrupt()方法中斷其運行狀態

setPriority(Thread.MIN_PRIORITY);  //設置線程的優先級爲最低(NORM_PRIORITY中等,MAX_PRIORITY最高) 

【注意】並非優先級越高就一定會先執行,哪個線程先執行將由 CPU 的調度決定

yield();   //線程禮讓,將一個線程的操作暫時讓給其他線程執行

wait();   //線程等待,屬於Object類,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法

 

創建多線程程序的第二種方式:

實現Runnable接口

實現步驟:

1.創建一個Runnable接口的實現類
2.在實現類中重寫Runnable接口的run方法
3.創建一個Runnable接口的實現類對象
4.創建Thread類對象,構造方法中傳遞Runnable接口的實現類對象
5.調用Thread類對象的start方法

package demo1;

public class ImpRunnable implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<5;i++) {
			System.out.println("thread:"+i);
		}
	}

}
package demo1;

public class myDemo12 {
	public static void main(String[] args) throws Exception {
		ImpRunnable imp = new ImpRunnable();
		Thread run = new Thread(imp);
		run.start();
	}
}

或者使用匿名內部類

new Thread(new Runnable() {
			public void run() {
				for(int i=5;i<10;i++) {
					System.out.println("main:"+i);
				}
			}
		}).start();

或者用Lambda表達式替代匿名內部類

new Thread(()->{
	for(int i=5;i<10;i++){
	   System.out.println("main:"+i);
	}
  }).start();

使用Runnable接口的好處:
1.避免了單繼承的侷限性
2.增強程序的擴展性,降低了程序的耦合性(解耦)
(設置線程任務與開啓線程進行隔離)

 

 

 

提示:

可以通過重載線程類的構造函數來傳入參數,以便run方法使用

 

  • 同步與死鎖

     參考:https://www.cnblogs.com/java1024/archive/2019/11/28/11950129.html  

一個多線程的程序如果是通過 Runnable 接口實現的,則意味着類中的屬性被多個線程共享,那麼這樣就會造成一種問題,如果這多個線程要操作同一個資源時就有可能出現資源同步問題。

解決方法:

同步代碼塊

synchronized(同步對象){ 
  需要同步的代碼 
}
class MyThread implements Runnable{ 
    private int ticket = 5 ;    // 假設一共有5張票 
    public void run(){ 
        for(int i=0;i<100;i++){ 
            synchronized(this){ // 要對當前對象進行同步 
                if(ticket>0){   // 還有票 
                    try{ 
                        Thread.sleep(300) ; // 加入延遲 
                    }catch(InterruptedException e){ 
                        e.printStackTrace() ; 
                    } 
                    System.out.println("賣票:ticket = " + ticket-- ); 
                } 
            } 
        } 
    } 
}; 
public class SyncDemo02{ 
    public static void main(String args[]){ 
        MyThread mt = new MyThread() ;  // 定義線程對象 
        Thread t1 = new Thread(mt) ;    // 定義Thread對象 
        Thread t2 = new Thread(mt) ;    // 定義Thread對象 
        Thread t3 = new Thread(mt) ;    // 定義Thread對象 
        t1.start() ; 
        t2.start() ; 
        t3.start() ; 
    } 
};

運行結果:

賣票:ticket = 5;
賣票:ticket = 4;
賣票:ticket = 3;
賣票:ticket = 2;
賣票:ticket = 1;

 

同步方法

除了可以將需要的代碼設置成同步代碼塊外,也可以使用 synchronized 關鍵字將一個方法聲明爲同步方法。

 synchronized 方法返回值 方法名稱(參數列表){ 
 
 }
class MyThread implements Runnable{ 
    private int ticket = 5 ;    // 假設一共有5張票 
    public void run(){ 
        for(int i=0;i<100;i++){ 
            this.sale() ;   // 調用同步方法 
        } 
    } 
    public synchronized void sale(){    // 聲明同步方法 
        if(ticket>0){   // 還有票 
            try{ 
                Thread.sleep(300) ; // 加入延遲 
            }catch(InterruptedException e){ 
                e.printStackTrace() ; 
            } 
            System.out.println("賣票:ticket = " + ticket-- ); 
        } 

    } 
}; 
public class SyncDemo03{ 
    public static void main(String args[]){ 
        MyThread mt = new MyThread() ;  // 定義線程對象 
        Thread t1 = new Thread(mt) ;    // 定義Thread對象 
        Thread t2 = new Thread(mt) ;    // 定義Thread對象 
        Thread t3 = new Thread(mt) ;    // 定義Thread對象 
        t1.start() ; 
        t2.start() ; 
        t3.start() ; 
    } 
};

運行結果:

賣票:ticket = 5;
賣票:ticket = 4;
賣票:ticket = 3;
賣票:ticket = 2;
賣票:ticket = 1;

 

死鎖

所謂死鎖,就是兩個線程都在等待對方先完成,造成程序的停滯,一般程序的死鎖都是在程序運行時出現的。

 

 

參考:java技術手冊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章