1. 程式人生 > >【Java程式設計】Java多執行緒的實現

【Java程式設計】Java多執行緒的實現

多執行緒

程式:是一個指令的集合。

程序:正在執行中的程式,是一個靜態的概念。

執行緒:是程序中的一個單一的連續控制流程,執行緒又本稱為輕量級程序。

一個程序可擁有多個並行的執行緒,一個程序中的執行緒共享相同的記憶體單元,記憶體地址空間,可以訪問相同的變數和物件,而且他們從同一堆中分配物件,通訊,資料交換,同步操作。

由於執行緒間的通訊是在同一地址空間上進行的,所以不需要額外的通訊機制,這就使得通訊更簡便,而且資訊傳遞速度也更快。

java多執行緒實現的方法有兩種。

第一種:

繼承Thread類,重寫run方法,建立物件,呼叫start方法啟動多執行緒。

第二種:

實現Runnable介面,重寫run方法,建立物件,呼叫start方法啟動多執行緒。

實現Runnable介面的類不能直接呼叫start方法,需要建立一個Thread物件,然後把物件傳進去,在呼叫start方法。

繼承Thread類開啟多執行緒:

注意一點,在開啟多執行緒的時候需要呼叫start,不能直接呼叫run方法,因為在開啟多執行緒前,需要一些準備工作,這些工作就需要start來做,start在做完這些準備工作後在去呼叫run方法。

/**
 * 使用多執行緒列印到1-100
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 繼承Thread類然後重寫run方法
	 * 
	 * @see java.lang.Thread#run()
	 */
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println("執行緒:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start();// 注意多執行緒的開啟需要呼叫start,不能直接呼叫run。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}
實現Runnable介面開啟多執行緒

實現Runnable介面不能直接呼叫start方法,因為start方法屬於Thread的,所以需要new一個Thread然後把物件傳進去,通過Thread呼叫start方法

/**
 * 使用多執行緒列印到1-100
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	/*
	 * 實現Runnable介面然後重寫run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		for (int i = 0; i <= 100; i++) {
			System.out.println("執行緒:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		new Thread(t).start(); // 注意實現Runnable介面物件不可以直接呼叫start。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}


常見的方法:

sleep 睡眠    讓執行緒睡眠一定的時間。它有兩種睡眠時間,一種是毫秒,一種是納秒

/**
 * 多執行緒
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 繼承Thread類然後重寫run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		try {
			sleep(1000); // 讓執行緒睡眠1秒,1000毫秒等於一秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			try {
				sleep(0, 1000); // 每列印一次讓執行緒睡眠1000納秒,當然速度太快,看不出效果。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("執行緒:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start(); // 注意實現Runnable介面物件不可以直接呼叫start。
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

 join插隊     任何一個執行緒在呼叫join後,那麼他就有優先執行的權利,其他的執行緒都要等到呼叫join方法的執行緒執行完畢,才可以執行。

通過上面的程式碼可以看出run方法在執行的是後遇到了sleep所以睡眠了,這是main繼續執行完畢,但是在呼叫join後main方法就只有等到run方法執行完畢才能繼續執行

/**
 * 多執行緒
 * 
 * @author FengYuan
 *
 */
public class Test extends Thread {
	/*
	 * 繼承Thread類然後重寫run方法
	 * 
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		try {
			sleep(1000); // 讓執行緒睡眠1秒,1000毫秒等於一秒
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			try {
				sleep(0, 1000); // 每列印一次讓執行緒睡眠1000納秒,當然速度太快,看不出效果。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("執行緒:" + i);
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		t.start(); // 注意實現Runnable介面物件不可以直接呼叫start。
		try {
			t.join(); // 呼叫join
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for (int i = 0; i <= 100; i++) {
			System.out.println("main方法::" + i);
		}
	}
}

yield讓步    正在執行的執行緒給給另一個執行緒讓一下,然後繼續執行,只是讓了一步。

呼叫方法:

Thread.yield();

wait 等待  然正在執行的執行緒進入等待,需要被喚醒,沒有喚醒將會一直等待下去,不會自己醒來。

使用方法:

t.wait();

finally 喚醒等待的執行緒  

finallyAll喚醒所有等待的執行緒。

t.finally(); // 喚醒一個等待的執行緒
t.finallyAll(); // 喚醒所有等待的執行緒
下面給一個多執行緒使用的例子:

現有100張火車票,讓5個人去賣票,直到賣完為止。

程式碼如下:

/**
 * 多執行緒(賣火車票)
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	int ticket = 100; // 100張火車票
	/*
	 * 實現Runnable介面然後重寫run方法 因為火車票共享所以必須用Runnable
	 * 
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public void run() {
		// 獲取當前執行緒的名稱
		String name = Thread.currentThread().getName();
		if (ticket > 0) {
			System.out.println(name + "賣出的第" + ticket + "張票");
			ticket--; // 每賣出一張減1
		} else {
			System.out.println("票買完了");
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		for (int i = 0; i <= 22; i++) {
			new Thread(t, "1號").start();
			new Thread(t, "2號").start();
			new Thread(t, "3號").start();
			new Thread(t, "4號").start();
			new Thread(t, "5號").start();
		}
	}
}

從上面的程式碼執行後我們可以看出賣的票有好多重複的,這就好比多個人同時買到的一張火車票,這樣的話是不是存在很大的問題,所以有給出了一個方法,可以避免這種情況的發生。

synchronize執行緒鎖,給執行緒加一把鎖,在看效果。

程式碼如下:

/**
 * 多執行緒(賣火車票)
 * 
 * @author FengYuan
 *
 */
public class Test implements Runnable {
	int ticket = 100; // 100張火車票
	/*
	 * 實現Runnable介面然後重寫run方法 因為火車票共享所以必須用Runnable
	 * 
	 * @see java.lang.Runnable#run()
	 */

	@Override
	public synchronized void run() {
		// 獲取當前執行緒的名稱
		String name = Thread.currentThread().getName();
		if (ticket > 0) {
			System.out.println(name + "賣出的第" + ticket + "張票");
			ticket--; // 每賣出一張減1
		} else {
			System.out.println("票買完了");
		}
	}

	public static void main(String[] args) {
		Test t = new Test();
		for (int i = 0; i <= 22; i++) {
			new Thread(t, "1號").start();
			new Thread(t, "2號").start();
			new Thread(t, "3號").start();
			new Thread(t, "4號").start();
			new Thread(t, "5號").start();
		}
	}
}

加鎖之後的程式碼我們再次執行就會發現,票沒有重複的了。

這是因為線上程沒有上鎖的情況下,就會有多個執行緒進同時進入方法,這樣就會造成重複,而枷鎖之後,執行緒每次只能執行一個,一個執行緒執行完後才能進入下一個執行緒。