1. 程式人生 > >多線程(一)

多線程(一)

任務管理器 應用程序 public 多線程 start

這邊來談談java中,我對對多線程的理解


在了解多線程前,先說說進程。

進程就是正在運行的應用程序。 當你打開任務管理器的時候,你就會發現很多的進程。

而我們要說的線程,就是依賴於進程而存在的,一個進程可以開啟多個線程。


Thread類

說到線程,就必須來說說Thread類。

Thread類是說有線程的父類。具體請參見api


線程的創建以及執行(圖解如下)

技術分享

繼承Thread類,或者實現rennable接口。
當繼承了父類後,需要重寫父類的run方法,這個run方法裏面就寫你要執行的代碼,當這個線程啟動的時候,就會執行你重寫的run方法裏面的內容。上邊的圖run裏面寫的是一個for循環,輸出的是一到一百。

線程啟動:start方法即可實現。
代碼實現:開啟線程,並啟動,輸出結果

public class MyThread extends Thread{
	//1.繼承Thread類
	//2.重寫run方法,重寫run方法中的代碼之後,當我們啟動了這個線程之後,我們的這個線程就會執行run方法中的代碼
	public class MyThread extends Thread{
	//1.繼承Thread類
	//2.重寫run方法,重寫run方法中的代碼之後,當我們啟動了這個線程之後,我們的這個線程就會執行run方法中的代碼
	@Override
	public void run() {
		//需求:開啟該線程之後,執行一個for循環
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}
	}
	
	//在測試類中啟動,來看輸出。
	//代碼實現
	public class Test {
	public static void main(String[] args) {
		//只要我們創建了一個線程對象,並且啟動該線程的實例,我們就相當於開啟了一///個線程
		MyThread mt = new MyThread();
		mt.start();//1.開啟了一個線程   2.讓開啟的這個線程執行他對應的類中的run方//法
		
		//在次創建一個子線程,並開啟這個子線程執行他的run方法
		MyThread mt2 = new MyThread();
		mt2.start();
		
	}
	
	最終輸出的是我們在run方法裏寫的for循環。
	
	
接下來我們用第二種方法,實現一個runnable接,並重寫run方法
public class MyThread implements Runnable{
	@Override
	public void run() {
		//啟動該線程對象之後,需要執行的代碼
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}	
	}

}	
public class MyThread implements Runnable{
	@Override
	public void run() {
		//啟動該線程對象之後,需要執行的代碼
		for (int i = 0; i < 10; i++) {
			System.out.println(i);
		}	
	}

}

public class Test {
	public static void main(String[] args) {
		//創建Mythread對象
		MyThread mt = new MyThread();
		Thread t1 = new Thread(mt);
		t1.start();
		
		
	}

}	
這就是線程的創建和啟動


	線程的調度和控制
		線程休眠(Thread.sleep(毫秒值))
		線程名稱(setName(),getName();)
		線程的調度及優先級setPriority(10)(註意默認值是5,區間在1-10之間)
		什麽叫線程優先級:說白了就是設置你搶占cpu執行權搶占到的概率
		
	
			MyThread t1 = new MyThread();
		MyThread t2 = new MyThread();
		MyThread t3 = new MyThread();
		
		//給三個線程設置姓名
		t1.setName("劉備");
		t2.setName("張飛");
		t3.setName("關羽");
		
		//設置線程的優先級
		//線程的調度及優先級setPriority(10)(註意默認值是5,區間在1-10之間)
		//t1.setPriority(100);//設置的區間必須在1-10之間
		t1.setPriority(10);
		
		//開啟線程
		t1.start();
		t2.start();
		t3.start();
		
		
	//共有100張票,將ticket改為靜態之後,被類的所有對象所共享
	static int ticket = 100;
	
	@Override
	public void run() {
		//用一個while true循環模擬三個窗口一直處於打開的狀態
		while (true) {
			//只有當ticket>0的時候,才可以出售票
			if (ticket>0) {
		System.out.println(getName()+"正在出售第:"+ticket--+"張票");
			}
		}
	}
		
		
	public static void main(String[] args) {
		//創建三個線程模擬三個售票窗口
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		MyThread mt3 = new MyThread();
		
		//給線程設置名稱
		mt1.setName("窗口一");
		mt2.setName("窗口二");
		mt3.setName("窗口三");
		
		//啟動線程,開啟售票
		mt1.start();
		mt2.start();
		mt3.start();
		
	}
		以上代碼是一個售票案例,是一個多線程的案例。
		
		
	這裏說到cpu的執行權。下圖簡單說說,執行權和執行。		

技術分享

這是那個案例的輸出結果,在之後我們會談到線程不安全性問題。

技術分享

這裏說幾點:執行權的搶占是隨機的,誰搶到就執行誰,可以通過sleep來驗證(詳見java相關\第十五天代碼+資料)


在使用runnable實現售票案例的時候,在線程睡一會之後,會出現線程安全性問題。


如何解決多線程安全問題

*線程安全執行效率就低

A:同步代碼塊(測試不是同一個鎖的情況,測試是同一個鎖的情況)

synchronized(對象) {

需要被同步的代碼。

}

需求:1.測試不是同一把鎖的時候線程安全嗎? 2.如果是同一把鎖線程安全嗎?

兩個問題:1.對象是什麽 ?

答:任意對象 ,相當於是一把鎖,只要線程進去就把鎖鎖上

2.需要同步的代碼?

答:被線程執行的代碼

鎖對象問題

a:同步代碼


塊(定義一個抽象類,裏面專門定義一個鎖)

任意對象

b:同步方法(僅適用於實現runable接口)

public synchronized void sellTicket(){同步代碼}

this

靜態同步方法

類的字節碼對象

public static synchronized void sellTicket() {

需要同步的代碼

}

public class MyThread implements Runnable{
//定義100張票
int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while (true) {
//同步代碼塊
//synchronized (new Object()) {//t1,t2,t3三個線程不共享同一把鎖每個線程都有自己的議案鎖
synchronized (obj) {//這樣3個線程才可以共享同一把鎖
if (ticket>0) {
//考慮到實際的生活中,我們需要給每一個線程加入一定的延遲,模擬一下這種效果
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第:"+ticket--+"張票");
}

//當被同步的代碼執行完畢之後,t1手裏拿著的obj這個鎖才會被釋放,

//t1,t2,t3重新搶占cpu的執行權,誰搶到了繼續拿著obj這個鎖,執行同步代碼塊中的內容

}

}

總之,要解決線程安全性問題,就是上邊的方法,關於線程,有好多知識點需要鞏固,當然有些東西會在下一次的文章中寫(多線程二)。

多線程(一)