1. 程式人生 > >多執行緒基本知識

多執行緒基本知識

一、基本概念

一個應用程式就是一個程序,作業系統上可以有多個程序,一個程序中可以有多個執行緒。

一個CPU一個時刻只能執行一個執行緒,當有多個CPU時可以同時並行執行多個執行緒,但是執行緒數大於CPU數時,會有執行緒處於阻塞狀態。

二、執行緒的生命週期

狀態:新建、就緒、執行、阻塞、死亡。

阻塞:執行緒沒有獲取CPU資源,不能執行


三、控制執行緒狀態

join()方法:在thread1中執行thread2_instance.join()方法時,thread1執行緒阻塞,thread2執行緒進入執行狀態;只有當thread2結束後thread1才會執行。

sleep()方法:thread1中呼叫Thread.sleep()方法時,thread1執行緒進入阻塞狀態,不釋放鎖

yield()方法:thread1中呼叫Thread.yield()方法時,thread1進入就緒狀態。

wait()、noitfy()、notifyAll()方法

四、執行緒同步

同步問題:當多個執行緒併發修改某個物件時,就會出現同步問題。

控制同步:

執行緒的目的是操作物件,那麼線上程類中把“目標物件作為成員變數”,然後“使用程式碼來操作、修改物件”。

使用鎖來控制同步,當某執行緒獲取鎖之後,只有執行完同步程式碼(中間沒呼叫wait()方法或沒有被異常中斷)才釋放鎖,其它執行緒才會獲取鎖來操作鎖物件。

1、同步程式碼塊

把操作修改物件的程式碼塊變成同步程式碼塊,目標物件作為鎖物件。

2、同步方法

把操作修改物件的程式碼塊變到物件類的例項方法中,把例項方法變成同步方法。通過物件呼叫此方法修改自己時,自己就是鎖物件。

3、使用lock物件

把可能出現同步問題的程式碼塊上鎖,執行完後解鎖。能保證

package grammar;

import java.util.concurrent.locks.ReentrantLock;

//被共同訪問、修改的物件
class Account {
	public int num;
	//鎖物件
	private final ReentrantLock lock = new ReentrantLock();

	public Account(int num) {
		this.num = num;
	}
	//呼叫此方法的物件就是“上鎖物件”,執行緒獲得該物件才能呼叫此方法
//	public synchronized void draw(int drawAmout){
	public void draw(int drawAmout){
		//加鎖,使用Lock物件作為鎖物件
		lock.lock();
		if (num >= drawAmout) {
			System.out.println("開始取錢");
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			num = num - drawAmout;
			System.out.println(num);
			System.out.println("結束取錢");
		//釋放鎖
		lock.unlock();	
		}
	}
}

// 修改物件的執行緒
class DrawThread extends Thread {
	// 被共訪修改的物件
	private Account account;
	private int drawAmout;

	public DrawThread(Account account, int drawAmount) {
		this.account = account;
		this.drawAmout = drawAmount;
	}

	// 修改方法
	public void run() {	
		//account呼叫同步方法,則account是上鎖物件
		account.draw(100);
		
		/**同步程式碼塊
		//把共訪修改物件作為上鎖物件,只有執行緒獲得該物件才執行“同步程式碼塊”
		synchronized (account) {
			if (account.num >= drawAmout) {
				System.out.println("開始取錢");
				try {
					// 強制讓執行緒阻塞
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				account.num = account.num - drawAmout;
				System.out.println(account.num);
				System.out.println("結束取錢");
			}
		}
		*/
		
	}
}

public class MyThread {

	public static void main(String[] args) {

		Account account = new Account(100);

		DrawThread thread1 = new DrawThread(account, 100);
		DrawThread thread2 = new DrawThread(account, 100);
		thread1.start();
		thread2.start();

		try {
			// main執行緒阻塞,先不列印
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		System.out.println("主執行緒");
		System.out.println(account.num);
		System.out.println("主執行緒");

	}

}

五、執行緒間通訊

藉助Object的三個方法:wait()、notify()、notifyAll(),只能在同步方法(直接呼叫)和同步程式碼塊(鎖物件呼叫)中呼叫。

wait()方法:在thread1中使用鎖物件.wait()方法,thread1進入阻塞狀態,釋放鎖。被notify()喚醒or超時後依然處於阻塞態,只有再次獲取鎖後才處於就緒態。而sleep()的執行緒醒後就是就緒態。

notify()方法:在thread1中使用鎖物件.notify()方法通知被wait()的執行緒後,thread1先執行完,再釋放鎖,其它需要獲得此鎖的執行緒才可能執行。若不使用notify()方法通知被wait()的執行緒,即使thread1執行完了釋放鎖,被wait()了的執行緒(超時後醒來可去競爭鎖物件)任然不能和其它執行緒進行競爭,從而獲得執行機會。