考試大綱之Java多線程

多線程是Java語言的一個重要特徵,利用多線程技術可以使系統同時運行多個程序塊,縮短了程序的響應時間,提高了計算機資源的利用率,達到了多任務處理的目的。

進程與線程
  • 進程:進程是程序的一次動態執行過程,每個程序都有自己獨特的內存空間,一個應用程序可以啓動多個應用空間,我們拿Chrome來舉例。
    在這裏插入圖片描述
  • 線程:線程是進程中的一個執行流程,一個進程可以由多個線程組成,即一個進程中可以同時運行多個不同的線程,每個線程完成不同的任務。

沒有進程就不會有線程,進程與線程是整體與局部的關係,進程與線程的關係如圖1-1所示。
在這裏插入圖片描述

圖1-1
線程的生命週期

線程在完整的生命週期中要經歷5種狀態,分別是新建、就緒、運行、阻塞和死亡。狀態關係如圖1-2所示。
在這裏插入圖片描述

圖1-2
實現線程的兩種方式
  1. 繼承Thread類
    在java.lang包中定義了Thread類,一個類繼承了Thread類,此類就稱爲多線程實現類。
    例子:
    Test_Thread.java
package com.hcybx;

public class Test_Thread extends Thread{//繼承Thread
   private String name;
   public Test_Thread(String name) {
   	this.name = name;
   }
   
   public void run() {//覆蓋Thread中的run()方法
   	for (int i = 1; i < 5; i++) {
   		System.out.println(name+"運行,i="+i);
   	}
   }
}

Test.java

package com.hcybx;

public class Test {
	public static void main(String[] args) {
		Test_Thread tt1 = new Test_Thread("A");//實例化線程對象
		Test_Thread tt2 = new Test_Thread("B");
		Test_Thread tt3 = new Test_Thread("C");
		Test_Thread tt4 = new Test_Thread("D");
		Test_Thread tt5 = new Test_Thread("E");
		Test_Thread tt6 = new Test_Thread("F");
		Test_Thread tt7 = new Test_Thread("G");
		Test_Thread tt8 = new Test_Thread("H");
		tt1.run();//啓動多線程
		tt2.run();
		tt3.run();
		tt4.run();
		tt5.run();
		tt6.run();
		tt7.run();
		tt8.run();
	}
}

運行結果:
在這裏插入圖片描述
2. 實現Runnable接口(使用多個線程共享資源,利用Runnable來完成)
通過實現Runnable接口來實現多線程。
Runnale接口的定義:

package com.hcybx;

public class Test_Runnable implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		
	}
	
}

利用Runnable子類來啓動多線程分爲三個步驟。
1.實例化Runnable子類對象。
2.利用Runnable對象作爲參數,實例化Thread對象。
3.利用Thread對象的start方法啓動線程。

繼承Thread類和實現Runnable接口的資源共享區別。(金典例題:賣飛機票)

1.通過繼承Thread類來實現賣飛機票

package com.hcybx;

public class ThreadSell extends Thread {
	private int ticket = 3;// 假如還剩三張飛機票
	private String sellName;// 售票機

	public ThreadSell(String sellName) {
		this.sellName = sellName;
	}

	public void run() {
		for (int i = 0; i < 20; i++) {
			if (ticket > 0) {
				System.out.println(sellName + "賣出第" + ticket-- + "張票");
			}
		}
	}
}

2.通過實現Runnable接口來實現賣飛機票

package com.hcybx;

public class RunnableSell implements Runnable {
	private int ticket = 3;

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if (ticket > 0) {
				System.out.println(Thread.currentThread().getName() + "賣出第" + ticket-- + "張票");
			}
		}

	}

}

3.啓動線程

package com.hcybx;

public class SellEngine {
	//Thread實現多線程
	public static void sellThread() {
		ThreadSell sell1 = new ThreadSell("售票機一");
		ThreadSell sell2 = new ThreadSell("售票機二");
		ThreadSell sell3 = new ThreadSell("售票機三");
		ThreadSell sell4 = new ThreadSell("售票機四");
		ThreadSell sell5 = new ThreadSell("售票機五");
		sell1.start();
		sell2.start();
		sell3.start();
		sell4.start();
		sell5.start();
	}
	
	//Runnable實現多線程
	public static void sellRunnable() {
		RunnableSell sell = new RunnableSell();
		Thread t1 = new Thread(sell, "售票機一");
		Thread t2 = new Thread(sell, "售票機二");
		Thread t3 = new Thread(sell, "售票機三");
		Thread t4 = new Thread(sell, "售票機四");
		Thread t5 = new Thread(sell, "售票機五");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}

4.開始賣票

package com.hcybx;

public class TestSell {
	public static void main(String[] args) {
		System.out.println("Thread方式");
		SellEngine.sellThread();
		try {
			Thread.sleep(2000);//線程執行暫停2秒
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("Runnable方式");
		SellEngine.sellRunnable();
	}
}

運行結果:
在這裏插入圖片描述

所以我們在實際開發中,實現Runnable接口相比繼承Thread類可以避免單繼承帶來的侷限。因此一般採用實現Runnable接口來實現多線程。

同步

再次回到飛機票的問題,假設全國飛機票系統中,存在多個人同時訂同一個機次的飛機票,那麼機票是共享資源,每個人的預購系統是一個線程,這樣存在多個線程對一個資源進行操作的問題。
問題引出:

package com.hcybx;

public class SellTicket implements Runnable{
	private int ticket = 5;  //假設還有5張票
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			if (ticket > 0) {
				try {
					Thread.sleep(1000);//延遲
				} catch (Exception e) {
					e.printStackTrace();
				}
				System.out.println("賣出第"+ticket--+"張票");
			}
		}
		
	}
	
}

package com.hcybx;

public class TestSell {
	public static void main(String[] args) {
		SellTicket st = new SellTicket();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		Thread t3 = new Thread(st);
		t1.start();
	    t2.start();
	    t3.start();
	}
}

多次運行結果:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

出現上面的根本原因就是程序代碼被多個線程共享而交替運行,解決它的關鍵是==確保共享的代碼塊在某個時間被一個線程所擁有。==在Java語言中就可以利用同步解決問題。

package com.hcybx;

public class SellTicket implements Runnable {
	private int ticket = 5; // 假設還有5張票

	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			synchronized (this) {
				if (ticket > 0) {
					try {
						Thread.sleep(1000);// 延遲
					} catch (Exception e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"賣出第" + ticket-- + "張票");
				}
			}
		}
	}
}

package com.hcybx;

public class TestSell {
	public static void main(String[] args) {
		SellTicket st = new SellTicket();
		Thread t1 = new Thread(st,"售票點一");
		Thread t2 = new Thread(st,"售票點二");
		Thread t3 = new Thread(st,"售票點三");
		t1.start();
	    t2.start();
	    t3.start();
	}
}

在這裏插入圖片描述
在這裏插入圖片描述
使用同步代碼塊就可以解決共享資源時的正確性問題啦!

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