Java基礎--併發編程基礎(4)

1.死鎖

線程同步的時候會對對象監視器所監視的操作上鎖,也就是隻有當前線程能夠進來,其他線程是進不來的,除非你拿到了監視器。
死鎖:當線程A進入到了X對象的監視器內,線程B進入到了Y對象的監視器內,X對象的監視器內部調用了Y對象監視器內部的操作,所以線程A想正常終結的話,必須等線程B交出監視器(終結或掛起(掛起不考慮)),然後以可重入鎖的方式進入Y對象監視器的內部(可重入鎖的概念,後續博文會解釋),執行完相關操作,然後終結;而巧的是Y對象的監視器調用了X對象監視器內部的操作,B線程若是想正常終結的話,必須等A線程交出監視器,這樣互相等待的狀態稱爲死鎖。
死鎖案例:
public class DeadLockTest {

	public static void main(String[] args) {
		/*
		 * 所謂死鎖,就是線程A進入到X對象的監視器內部,線程B進入到了Y對象的監視器內部,而A在X監視器內部需要調用B的監視器內部操作才能結束,所以就調用了,然而,得等B交出監視器(B終結或者掛起(掛起除外))
		 * 而巧的是B要想終結的話,得執行A監視器內部的操作才能執行,這樣就造成了互相等待,而且不會有解,除非有一個能夠不以終結的方式交出監視器
		 */
		Task1 t1 = new Task1();
		Task2 t2 = new Task2();
		new Thread(()->{
			t1.task1(t2);
		}).start();
		new Thread(()->{
			t2.task1(t1);
		}).start();
//		運行結果:
//		Task1...task1
//		Task2...task1
//		正在運行..(必須強行終止否則沒有頭啊)
	}
	
}
class Task1{
	public synchronized void task1(Task2 task2){
		System.out.println("Task1...task1");
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		task2.task2();
	}
	public synchronized void task2(){
		System.out.println("Task1...task2");
	}
}
class Task2{
	public synchronized void task1(Task1 task1){
		System.out.println("Task2...task1");
		try {
			Thread.sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
		task1.task2();
	}
	public synchronized void task2(){
		System.out.println("Task2...task2");
	}
}

2.線程狀態間轉化

線程總共有5個狀態:被創建後還沒有被執行狀態、執行狀態、阻塞狀態(等待,不交出鎖)、等待狀態、終結態
相互之間的轉化圖如下:
調用sleep方法後處於等待狀態,但是不交出監視器持有;調用wait方法後也是處於等待狀態,但是交出監視器持有。

3.線程組

線程組爲管理一組線程提供了一種便利的方式,可以把一組線程當成一個單位進行管理。對於希望對一組線程進行操作(掛起、恢復等)是很便利的。
線程組類:ThreadGroup(String groupName) ThreadGroup(ThreadGroup parentGp,String groupName)
對於如何設置線程所屬的線程組,以及如何獲取線程組中的線程由下文的代碼實例說明。
對於線程組的理解,可以從Java中線程的組織關係下手:
1.Java中線程是以樹的結構進行管理的,樹的根是主線程,由主線程創建的線程是根的孩子,由此遞歸
2.這顆樹上的線程都是活着的線程:沒有終結也沒有掛起
3.線程組在這個樹中是什麼呢?線程組是非葉子節點。如果沒有加入線程組的話,所有的子線程都是根(主線程)的孩子,加了線程組,那所有的線程(除主線程)都是葉子節點,從樹的不同非葉子節點(線程組)總是能獲取這個節點的所有子孫節點(子線程),說明代碼如下:
實例代碼如下:
public class ThreadGroupTest {

	public static void main(String[] args) {
		/*
		 * 對於任何一個程序,首先有一個主線程,這個主線程是程序主函數(入口函數)啓動的時候啓動的
		 * 由主線程M開的線程A之間的關係是:M和A位於同一個線程組(即使在開新線程的時候不指明線程組的話),M是A的父線程
		 * 同樣,如果A線程再開線程,那開的線程就是A線程的子線程
		 * 可用的三個構造器分別爲:
		 * Thread(ThreadGroup groupOb,Runnable threadOb):指明線程組和Runnable實現
		 * Thread(ThreadGroup groupOb,Runnable threadOb,String threadName):指明線程租和Runnable實現和線程名稱
		 * Thread(ThreadGroup groupOb,String threadName):指明線程組和線程名稱,通常在Thread子類的構造器中調用super方法時使用這個
		 * 引入線程組的目的是爲了批管理線程
		 */
		//t1和主線程位於同一線程組
		Thread t1 = new Thread(()->{
			//t1_1和主線程位於同一線程組
			Thread t1_1 = new Thread(()->{
				//加上死循環是爲了保證這個線程一直在這個線程組中活下去,已經終結的線程是不能通過線程組獲取的
				while(true);
			});
			t1_1.start();
			while(true);
		});
		t1.start();
		//t2屬於線程組X
		Thread t2 = new Thread(new ThreadGroup("X"),()->{
			Thread t2_2 = new Thread(new ThreadGroup("Y"),()->{
				while(true);
			});
			t2_2.start();
			while(true);
		});
		t2.start();
		//獲取這兩個線程租
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		//獲取展示這兩個線程組中都有哪些線程
		tg1.list();
		tg2.list();
		//分別獲取到每個線程組中的每個線程
		Thread[] threadarray1 = new Thread[tg1.activeCount()];
		Thread[] threadarray2 = new Thread[tg2.activeCount()];
		tg1.enumerate(threadarray1);
		tg2.enumerate(threadarray2);
		//操作已經get到的線程組中的線程,這裏僅僅是打印出來,實際就是這樣進行批處理的哦
		System.out.println("Threads in the main Thread Group");
		for(Thread t:threadarray1){
			System.out.println(t);
		}
		System.out.println("Threads in the X Thread Group");
		for(Thread t:threadarray2){
			System.out.println(t);
		}
		
//		運行結果(帶#表示不是運行結果,是後加的註釋):
//		#樹結構:
//		java.lang.ThreadGroup[name=main,maxpri=10]					#從根節點獲取子線程
//			    Thread[main,5,main]
//			    Thread[Thread-0,5,main]
//			    java.lang.ThreadGroup[name=X,maxpri=10]
//			        Thread[Thread-1,5,X]
//			        java.lang.ThreadGroup[name=Y,maxpri=10]
//			            Thread[Thread-3,5,Y]
//			java.lang.ThreadGroup[name=X,maxpri=10]					#從X線程組獲取子線程
//			    Thread[Thread-1,5,X]
//			    java.lang.ThreadGroup[name=Y,maxpri=10]
//			        Thread[Thread-3,5,Y]
//			#數組表示:
//			Threads in the main Thread Group
//			Thread[main,5,main]
//			Thread[Thread-0,5,main]
//			Thread[Thread-2,5,main]
//			Thread[Thread-1,5,X]
//			Thread[Thread-3,5,Y]
//			Threads in the X Thread Group
//			Thread[Thread-1,5,X]
//			Thread[Thread-3,5,Y]
		
	}
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章