Java_線程,Timer,線程組以及同步的方法

進程和線程:
一個程序就是一個進程,可以打開資源管理器查看現在有多少個進程同時運行。
計算機是支持多進程的。
多線程的作用:
以遊戲爲例(需要一個線程來控制主角的移動,一個線程控制敵人的AI攻擊行爲),
當需要多個任務同時運行時,就要使用多線程。
主線程:
main方法處於一個默認的線程中,這個線程稱爲主線程,由系統默認創建出來(JVM創建啓動)。

創建額外的線程:

public class MyThread extends Thread{   //額外的線程類

@Override
public void run() { //線程的start()會自動調用該線程的run()
	for(int i = 1;i<=500;i++)
		System.out.println("MyThread :"+i);
}
}

在運行的主線程中開啓額外的線程:

public static void main(String[] args) {
	
	MyThread mt = new MyThread();
	mt.start();//right,調用線程類的run()會開啓一個線程,同時啓動run()
//	mt.run();//error,相當於調用一個普通方法,不開線程
	
	for(int i = 1;i<=500;i++)
		System.out.println("主線程 :"+i);
	
}

Thread.currentThread() 可以返回當前方法所處的線程,可以用該方法獲取main線程。

線程調度規則(線程調度會整體上是遵守下面的規則,但是從單個上來看是隨機的)
1、分時調度(平均分配)
2、搶佔式調度(按照優先級)—>Java使用的調度規則
優先級高的,有更高的機率被CPU所執行

Thread的常用方法:
.getPriority() 獲取當前線程對象的優先級
.setPriority() 設置當前線程對象的優先級,不在下述優先級會拋出異常
NORM_PRIORITY–>5,MAX_PRIORITY–>10,MIN_PRIORITY–>1

創建線程對象時系統會自動分配name,規則爲:
Thread-0,Thread-1…若爲主線程,name爲main
除非在new Thread(String name)時改名,否則分配的name會始終佔位。
可使用.setName(),.getName()更改和獲取線程對象的線程名。

線程休眠,讓當前線程休眠
Thread.sleep(); 使用Thread調用則使當前所處的線程休眠x毫秒,不使用對象進行調用!!
.join();等待該線程終止。若該對象線程被其他線程中斷則拋出異常。
.setDaemon(true)在start之前將該線程對象設置爲守護線程
如果程序中只剩下守護線程在運行,那麼程序會停止。
.interrupt() 讓線程處於中斷狀態,使用static boolean interrupted()配合使用判斷當前線程是否處於中斷狀態,並調整,釋放資源再終止自身的運行。

二、使用Runnable創建線程:
a)實現Runnable接口
b)實現run方法
c)創建當前類的對象和Thread的對象,並使用Thread對象的.start()啓動

其他
a)獲取當前線程(implements Runnable的Runnable)的名字
	Thread.currentThread().getName()
	設置名字通過Thread對象
b)構造方法
	Thread t = new Thread(Runnable target);
	Thread t = new Thread(Runnable target,String name);

使用implements Runnable較extends Thread創建線程的好處:
1、更方便進行數據的共享
2、方便後續繼續繼承其他類

三、使用匿名內部類的方式創建線程:
在某一方法中使用匿名內部類:

Runnable r = new Runnable() {
		@Override
		public void run() {
			for (int i = 1; i <= 50; i++)
				System.out.println(Thread.currentThread().getName() + ": " + i);
		}

	};
	new Thread(r, "線程1").start();

	new Thread("線程2") {
		public void run() {
			for (int i = 1; i <= 50; i++)
				System.out.println(getName() + ": " + i);
		}
	}.start();

線程安全問題:!!!!!
CPU在某一時間只能執行某個線程的一句代碼(以;結尾爲一句代碼)。
最重要的一點:
【線程start()後,運行線程的run方法體,只有方法體中的循環語句體會被重複執行,循環語句體外的語句只會被執行一次!】
如,在4個站點同時售票的問題:

//4個站點Thread共用的Runnable
/*僅當4個Thread都使用同一個Runnable r對象構建時(因爲這時4個Thread執行時需要運行的是
  共同的幾句關鍵代碼),可以使用下述synchronized上鎖來解決重複售票問題!!*/

private int ticket = 100;
//鑰匙s必須是Runnable僅有的一把鑰匙(不在run()中聲明)
private String s = new String();

public void run() {
	while (ticket > 0) {
		synchronized (s) {
			if (ticket > 0) {
			
			    /*下面兩句代碼如果不使用synchronized上鎖,可能會出現多站點
			      重複售出第100張票。【也可以不對這兩句代碼上鎖,而是將ticket--進行輸出,
			      (在cpu運行的這一時間點同時完成兩個任務)。】*/
			      
				System.out.println(Thread.currentThread().getName() + "賣出第" + ticket + "張票");
				ticket--;
			}
		}//在此處將鑰匙s歸還,並由於處於循環體中,每次賣出後進行sleep()
	
	
	try {
		System.out.println(Thread.currentThread().getName()+"等待了一次");
		Thread.sleep(100);
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	
	}//while ticket>0

}// run

線程安全問題:
多個線程同時要修改一個變量的時候,引起衝突.
線程安全問題解決

synchronized(Object key){
		//被上鎖的代碼行,只有拿到key的線程可以運行
		}
		/*synchronized代碼塊運行結束後自動歸還key,
		使key可以被 其他等待拿key的(執行上鎖代碼塊的)線程 爭奪。*/

對synchronized的深入理解:
在方法體內synchronized(Object key){ xxx代碼行 },key要爲多個線程所共有的對象且不能空指向 【在Thread中static可使某對象變爲多Thread對象的共有對象】纔可以鎖住 xxx代碼行 ,使其在某一時間段只被一個線程執行。 Object key也可爲this—>指當前類對象。
synchronized也可修飾方法,使該方法直接被上鎖。
【前述:
線程安全的類 :
安全: StringBuffer Vector
不安全:StringBuilder ArrayList
其之所以線程安全,就是因爲其方法被synchronized修飾,也導致其效率相當較慢。】

第二種解決線程安全的方法(同步鎖的第二種使用方式):
a)創建鎖對象 ReentrantLock lock = new ReentrantLock();
b)加鎖和解鎖
使用tryfinally
lock.lock();
lock.unlock();
注意事項:ReentrantLock對象 lock要爲多個線程所共有的。
如果lock()與unlock()之間上鎖的代碼行運行異常,則會導致無法解鎖其他線程持續等待。所以要對上鎖的代碼行try包裹,並finally包裹unlock()。

死鎖:
死鎖是這樣一種情形:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於線程被無限期地阻塞,因此程序不可能正常終止。

線程組ThreadGroup
默認創建的線程處於同一個組裏面。
使用線程組可以統一設置這個組內線程的一些東西。比如設置最大優先級,中斷其中的所有線程…
可以在創建線程時:Thread(ThreadGroup group, Runnable target, String name)將線程歸組。

特殊的線程 Timer
Timer是安排在後臺由於執行任務的線程,TimerTask使其要及執行的任務。

public class TimerDemo {

public static void main(String[] args) {
	
	Timer ti = new Timer();//創建一個Timer線程
	
	ti.schedule(new timerT(), 2000); //線程要執行的任務,延遲2000毫秒執行
	ti.schedule(new timerT(), 2000,2000);//延遲2000毫秒執行後,每隔3秒再執行一次
	//timer.schedule(TimerTask task, Date time)  -->在某一時間定時執行
	ti.cancel();//線程取消
}
}
class timerT extends TimerTask{
@Override
public void run() {
	System.out.println("定時執行的任務");
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章