黑馬程序員_【總結】_多線程知識梳理2(end)

多線程知識梳理2


---------- android培訓 java培訓、期待與您交流! ---------- 

---------------------------------------------------------------------------------------------------------------------------------------------

1、實際開發 匿名內部類使用很廣泛。
2、使用 Lock 替代 synchronized  顯示加鎖解鎖     >>     lock()、unlock()
3、利用 可以有多個 Condition 對象  進行指定signle 和 await
4、Condition中 await   signal     signalAll 替換原來Object中等待喚醒方法。
5、使用LOCK關鍵代碼:
private Lock lock  = new ReentrantLock();
private Condition conAA = lock.newCondition();
private Condition conBB = lock.newCondition();
6、清除凍結狀態:interrupt()   使用時,一定要配對更改 標籤。
7、守護線程、後臺線程setDaemon(boolean) 依附於主線程,
8、臨時加入線程 join() 能夠獲取執行權,被剝奪執行權的線程只有在該線程結束後纔會參加搶奪
執行join() 方法前的線程會和當前線程搶奪資源
9、線程組、優先級 setPriority設定優先級 記住MAX、MIN、MOR、NORM
10、暫停 線程 yield()  臨時的釋放執行權,減緩線程的運行頻率,保證每個線程能較爲平均的運行
-------
11、同步代碼的鎖,最好的是該類中 最唯一的對象  r  最具唯一性
12、共享數據 都必須同步  synchronized(r)  2處都是共享數據
13、通過一個 flag標記,來判斷,一個線程是否結束, 通過 wait 等待  和 notify 喚醒機制,
14、wait()  notify() notifyAll    必須同一把鎖  ,鎖可以是任意的對象,而被任意對象調用的方法定義在Object中
15、notify() 適合僅僅2個線程的時候。當大於2個線程的時候,由於喚醒機制,容易出現安全問題。
16、當同時有多個線程,執行相同功能時候,必須寫 while 循環判斷標記 使用 notifyAll 喚醒機制
-------------------------------------------------------
【1】
多線程中存在的安全問題
//例子1--2個線程。
class names2 
{
	public static void main(String[] args) {
		Res r = new Res();
		new Thread(new input(r)).start();
		new Thread(new output(r)).start();
	}
}
class Res{
	private String name;
	private String sex;
	boolean flag;
	public synchronized void set(String name,String sex){
		if(flag)
			try{this.wait();}catch(Exception e){}
		this.name = name;
		this.sex = sex;

		flag = true;
		notify();	
	}
	public synchronized void out(){
		if(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(name+">>"+sex);
		flag = false;
		notify();	
	}
}
class input implements Runnable
{
	private Res r;
	input(Res r){
		this.r = r;
	}
	public void run(){
		int x = 0 ;
		while(true){
			if(x == 0)
				r.set("lili","11111111");
			else
				r.set("達到","-----------");

			x = (x+1)%2;
		}
	}
}
class output implements Runnable{
	private Res r;
	private boolean flag;
	output(Res r){
		this.r = r;
	}
	public void run(){		
		while(true){
			r.out();
		}
	}
}
生產和消費  4個線程 使用上訴邏輯 顯然是不行的。
class shangping {
	public static void main(String[] args) {
		Resource r = new Resource();
		new Thread(new Producer(r)).start();
		new Thread(new Producer(r)).start();
		new Thread(new Consumer(r)).start();
		new Thread(new Consumer(r)).start();
	}
}
class Resource{
	private String name;
	private int num=1;
	private boolean flag;
	public synchronized void set(String name){
		while(flag)// 循環致使每一次都會判斷
			try{this.wait();}catch(Exception e){}
		this.name = name+".."+num++;
		System.out.println(Thread.currentThread().getName()+">>>"+this.name);
		this.flag = true;
		this.notifyAll();//喚醒全部線程,避免只喚醒本方線程
	}
	public synchronized void out(){
		while(!flag)
			try{this.wait();}catch(Exception e){}
		System.out.println(Thread.currentThread().getName()+"================="+this.name);
		this.flag = false;
		this.notifyAll();
	}
}
//sheng chan
class Producer implements Runnable{
	private Resource r ;
	Producer(Resource r ){
		this.r = r ;
	}
	public void run(){
		while(true){
			r.set("CPU");
		}
	}
}
class Consumer implements Runnable{
	private Resource r ;
	Consumer(Resource r ){
		this.r = r ;
	}
	public void run(){
		while(true){
			r.out();
		}
	}
}
例子2是對例子1進行了線程的擴編, 發現 notify侷限性,1、只會判斷標記一次,如果改爲while
又會出現全部臥倒的可能性。
可以看出,多線程的安全問題是一個需求十分嚴謹卻又痛苦萬分的。然後來看看更新優化
【2】
【2-1】1.5新特性:
將同步 synchronized 替換成 顯示 的 Lock 
將Objcet 中替換成Condition 對象    wait notify  notifyAll >>>>await   signal     signalAll
似乎沒有太大的更新,
但是 Lock 中可以有 多個 Condition 對象 
也就是說,在讀取標籤的時候,可以指定讓持有具體鎖的那個對象 await  或者 signal
這樣一樣,代碼就明朗了很多,不會出現只喚醒一方的情況。有效的避免了 全部臥倒。


替換後的代碼。其實思路是一樣了,不同的就是把加鎖,釋放鎖的過程放在了名面上。
class Resource{
	private String name;
	private int num=1;
	private boolean flag;
	private Lock lock  = new ReentrantLock();
	private Condition conAA = lock.newCondition();
	private Condition conBB = lock.newCondition();
	public void set(String name){
		lock.lock();
		try{
			while(flag)
				try{conAA.await();}catch(InterruptedException e){}//true>>讓A方都await
			this.name = name+".."+num++;
			System.out.println(Thread.currentThread().getName()+">>>"+this.name);
			flag = true;
			conBB.signalAll();// A方完事了>>>flag = true>>讓B方 醒
		}
		finally{
			lock.unlock();
		}		
	}
	public void out(){
		lock.lock();
		try {
			while(!flag)
				try{conBB.await();}catch(InterruptedException e){}// false>> 讓B都await
			System.out.println(Thread.currentThread().getName()+"================="+this.name);
			flag = false;
			conAA.signalAll();// B 方完事了>>>flag = false>>  讓A 方醒
		} 
		finally{
			lock.unlock();
		}
	}
}
InterruptedException : 當一個線程在運行中,被中斷的異常。


多線程的運行,通常都是循環結構,控制住 循環,就可以讓run方法結束,也就結束了線程。


特殊情況:
當線程處於凍結狀態,就不會讀取標記,那麼線程就不會結束


當沒有指定的方式讓凍結的線程恢復到運行狀態,這時需要對凍結狀態進行清除
強制讓線程恢復到運行狀態中來,這樣就可以操作標記讓線程結束。
【2-2】
Thread  提供方法: interrupt()   清除凍結狀態
使用時,1,interrupt清除,2改變標籤值。
class stop implements Runnable{
	private boolean flag = true;
	public synchronized void run(){
		while(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
				flag = false;//【2】清除凍結狀態後 改變標籤屬性 達到結束目的。
			}
			System.out.println(Thread.currentThread().getName()+">>>>RUN");
		}
	}
	public void changeFlag(){
		flag = false;
	}
}
public class interrupt {
	public static void main(String[] args) {
		stop s = new stop();
		Thread t1 = new Thread(s);
		Thread t2 = new Thread(s);
		t1.start();
		t2.start();
		int num = 0 ;
		while(true){
			if(num++ ==60){
				//s.changeFlag();
				t1.interrupt();//【1】 清除 凍結狀態
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"MAIN------------");
		}
		System.out.println("over");
	}
}
【2-3】
void setDaemon(boolean)
設置爲守護線程或用戶線程(後臺線程)前臺線程一結束,後臺線程都結束
必須設定在 start  前。
上面例子部分修改一下
	t1.setDaemon(true);//設定爲守護線程
	t2.setDaemon(true);
	t1.start();
	t2.start();
	int num = 0 ;
	while(true){
		if(num++ ==60){
			//t1.interrupt();//【1】 清除 凍結狀態
			break;
		}
		System.out.println(Thread.currentThread().getName()+"MAIN------------");
	}
當沒有 設定  守護線程   的時候,會出現等待現象,
而,當設定了後臺後, 主線程 一結束,t1  t2  也跟着結束了。

註釋掉 try 部分,   觀看是否設定 後臺線程  結果更爲明顯。
		while(flag){
		/*	try {
				wait();
			} catch (InterruptedException e) {
				System.out.println(Thread.currentThread().getName()+"!!!!!!!!!!!!");
				flag = false;//【2】清除凍結狀態後 改變標籤屬性 達到結束目的。
			}
		*/	System.out.println(Thread.currentThread().getName()+">>>>RUN");
		}
【2-4】
join() 臨時加入線程執行。
當進行多線程運算時,不論誰擁有執行權(假設B),當加入 A.join() 
當讀到A.join()  表示A線程要執行權,A 要優先執行。
A 線程結束後,才釋放執行權,這個時候B 纔會參與和他線程再相互搶奪執行權利
也就說說,A.join() 前只有A時, A執行 
 A.join() 前有A和C時, AC相互搶奪 執行權
 最開始拿到執行權利的B 只能等待A  執行完畢後,纔會參與權利搶奪。
public class jion {
	public static void main(String[] args) {
		jionD j = new jionD();
		Thread t1 = new Thread(j);
		Thread t2 = new Thread(j);
		t1.start();
		//t2.start();//當 t2在t1.join() 前時候,會和 t1 搶奪執行權。
		try {t1.join();} catch (InterruptedException e) {}
		t2.start();
		
		for(int i = 0 ;i <70 ; i++){
			System.out.println(Thread.currentThread().getName()+"-------"+i);
		}
	}
}
class jionD implements Runnable{
	public void run() {
		for(int i = 0 ;i< 70 ; i++){
			System.out.println(Thread.currentThread().getName()+">>"+i);
		}
	}
}
總之一句話,join() 會剝奪擁有當前執行權的線程,一直到join()  對應的線程結束纔會參與搶奪,至於 join()  對應的線程會不會被其他線程搶奪資源另說。
【2-5-1】
setPriority() 設定優先級
通過 Thread.currentThread().toString() 查看 到線程的優先級和線程組
ThreadGroup 線程組對象  據說用的很少
創建線程組,把線程裝入該對象,即是一個線程組
優先級 :1-10級 默認優先級 5
MAX_PRIORITY   >>10
MIN_PRIORITY   >>5
NORM_PRIORITY   >>1

根據上面的例子
t2.setPriority(MAX_PRIORITY); 
這個時候t2  線程的優先級最高,在運行越多時越明顯,但不代表 ,其他線程搶奪不到資源。

【2-5-2】
yield () 方法
暫停當前正在執行的線程對象,並執行其他的線程。
class jionD implements Runnable{
	public void run() {
		for(int i = 0 ;i< 70 ; i++){
			System.out.println(Thread.currentThread().getName()+">>"+i);
			Thread.yield();//上列代碼 修改部分
		}
	}
}
這個時候,t1 t2不論誰進來,會稍微減少線程運行頻率,並保證每一個線程都有機會平均性的運行。

【3】
老師給出的,實際開發中 常用的 線程方式, 感覺 匿名內部類的使用是非常普遍的。
public class ThreadTest {
	public static void main(String[] args) {
		//
		new Thread()
		{
			public void run(){
				for(int i = 0 ;i < 100 ; i++ ){
					System.out.println("匿名內部類線程");
				}
			}
		}.start();

		for(int i = 0 ;i < 100 ; i++ ){
			System.out.println("主線程部分");
		}
		//
		Runnable r = new Runnable()
		{
			public void run(){
				for(int i = 0 ;i < 100 ; i++ ){
					System.out.println("不同封裝方式達到同時運行的效果");
				}
			}
		};
		new Thread(r).start();
	}
}
//end














---------------------------------------------------------------------------------------------------------------------------------------------
---------- android培訓、 java培訓、期待與您交流!----------
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章