天天看點

【多線程】的學習總結

關于【多線程】的學習總結

作者:wkz_crystal基本的概念:多線程其實就是程序中一個獨立的控制單元或者說是執行路徑,線程控制着程序的執行,【重點】一個程序中,至少有一個線程存在。

【1】【線程的建立】怎樣繼承Thread類,建立多個線程?這個和實作Runnable接口建立的方式有什麼差別?

【2】建立線程為什麼要覆寫run方法和為什麼在main方法中執行線程程式,每一次運作的效果都不一樣?

【3】怎樣獲得目前線程的名稱?

【4】【重點】線程中存在的安全問題是什麼,怎樣發現線程安全?

【5】【重點】執行個體分析:汽車站售票程式【其中涉及了線程同步】

【6】同步的兩種表現形式是什麼?

【7】懶漢式單例模式分析

【8】多線程的死鎖問題

【9】JDK1.5中關于線程的新特性

【10】多線程之間的通信模式分析

【11】停止線程的方法

【12】join和yield方法總結

【13】開發過程中為了提高效率,怎樣單獨封裝?

我根據執行個體來總結:分享給大家

【1】繼承Thread類建立線程和實作Runnable接口來建立線程:

Thread類是一個線程類,我們在具體定義定義多線程開發時,有兩種方式建立線程,其中一種就是通過繼承的方式來完成,覆寫Thread類中的run方法:但是這種建立方式有一定的弊端:那就是被建立的子類不能再繼承其他的類;為了解決這種弊端,一般我們都直接去實作Runnable接口去實作多線程的建立,這其實也正是JAVA中解決多态性的具體方案,實作一個接口之後,也能去繼承其他的父類或者被子類繼承!

原理明白了,好了,寫個簡單的程式示範示範吧:

class Mythread extends Thread//這裡就是一個簡單的繼承建立線程的方式
{
	public void run()//覆寫了父類中的run方法,定義了自定義的函數主體for循環
	{
		for (int i=0;i<50 ;i++ )
		{
			System.out.println("thread hao!="+i);//僅僅是示範線程執行的内容
		}
		
	}
}
//這裡就是通過main方法來調用
class ThreadDemo1 
{
	public static void main(String[] args) 
	{
		Mythread mt=new Mythread();//把這個線程對象執行個體化
		mt.start();//調用父類的start方法,啟動并執行線程
	}
}
           
class Cus implements Runnable//這裡是通過實作Runnable接口的方式建立多線程的
{
	public void run()//同理也要覆寫接口的run方法
	{
		for (int i=0;i<3;i++ )//簡單的函數主體
		{
			System.out.println("....."+i);//用于示範而已
		}
	}
}

class SynchronizedDemo4 
{
	public static void main(String[] args) 
	{
		Cus c=new Cus();//建立接口的子類對象
		Thread c1=new Thread(c);//定義了兩個線程,把接口的子類對象和線程相關聯起來
		Thread c2=new Thread(c);
		c1.start();//啟動兩個線程并且執行
		c2.start();
	}
}
           

【2】為什麼要複寫run方法:

複寫run方法主要是因為:在Thread類中,run方法是儲存線程要運作的重要代碼,是以在定義的時候,我們必須根據具體的需求去覆寫run方法,進而自定義要實作的功能主體!

start方法是父類開啟和執行線程的方法,直接調用就行了,但是我們如果不覆寫run方法,直接在子類中調用的話,是毫無意義的,因為這樣的話,相當于我們隻是建立了線程,而沒有運作!

在main方法中,多次運作多線程的結果都不一定一樣的原因是:我們知道,多線程在執行的時候,都是在擷取cpu的一個執行權,cpu說想把執行權給誰(線程),誰(線程)就去執行相應的操作!但是在某一時刻,隻能有一個程式在運作,當然多核除外

這也就反應除了多線程的一個重要特性:那就是随機性!(可以了解為:誰搶到誰就執行)

【3】擷取目前線程的名稱:

很簡單了:用線程類的靜态方法就可以:

Thread.currentThread().getName():這樣就能夠獲得目前線程的名稱:

線程名稱的基本格式是:Thread-編号,編号都是從0開始的

【4】發現線程安全:

可以模拟出線程在運作中出現的問題:在這裡賣票系統中就是最好的分析

一般通過下列代碼就能找到其安不安全的地方!因為現在隻要在這裡休眠一下,其他線程自然會獲得cpu的執行權進來,這樣就沒法保證在共享的代碼中,不出現問題:在不同步的情況下,售票視窗可能會售出0号,甚至是-1号票,這樣就要求我們必須要保證共享資料的同步性:

對于發現線程的安全問題:

具體步驟:

分析:哪裡出現了線程的安全問題

如何去找原因:

1.明确哪些代碼是多線程運作的代碼:

2.明确哪裡是共享的資料

3.明确多線程的運作代碼中哪裡涉及到了要執行共享的資料

<span style="white-space:pre">					</span>//這種簡單的方式隻是模拟出現問題而已:
           
<span style="white-space:pre">					</span>try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
           

【5】汽車站售票小程式:

模拟賣票視窗,4個視窗同時賣100張共同的票

/*
實作Runnable接口的具體步驟和解決安全性的問題
*/

//1、繼承Thread類的做法:
/*
class Ticket extends Thread
{
	private static int tickets=100;  
	//為了確定票的唯一性,必須用static修飾,否則每個線程視窗會各自賣100張票

	//覆寫父類的run方法,定義線程賣票方式
	public void run()
	{
		while (true)
		{
			if (tickets>0) //當票大于0時,才能出票
			{
				//列印結果,擷取各線程(視窗)賣票情況
				System.out.println(Thread.currentThread().getName()+".....sale:"+tickets --);
			}
		}
	}
}

class ThreadDemo3 
{
	public static void main(String[] args) 
	{
		Ticket t1=new Ticket();
		Ticket t2=new Ticket();
		Ticket t3=new Ticket();
		Ticket t4=new Ticket();  //建立4個視窗(4個線程)
		
		t1.start();
		t2.start();
		t3.start();
		t4.start(); //啟動并執行視窗的賣票

		/*
		部分結果:
		Thread-2.....sale:76
		Thread-2.....sale:75
		Thread-2.....sale:74
		Thread-3.....sale:80
		Thread-1.....sale:81
		Thread-1.....sale:71
		Thread-1.....sale:70
		Thread-3.....sale:72
		Thread-2.....sale:73
		Thread-0.....sale:78
		Thread-2.....sale:67
		Thread-2.....sale:65
		Thread-3.....sale:68
		Thread-1.....sale:69
		Thread-3.....sale:63
		Thread-2.....sale:64
		
	}
}
*/
//實作Runnable接口
class Ticket implements Runnable
{
	private int tickets=100;
	Object obj=new Object();
	public void run()
	{
		while (true)
		{
			synchronized(obj)
			{
				if (tickets>0)
				{
					/*這裡是模拟cpu讓進來的某一個線程沒有配置設定到執行權,必然會售出不正常的票
					try
					{
						Thread.sleep(10);
					}
					catch (Exception e)
					{
					}
					*/

					//為了解決這樣的安全問題,要用到同步代碼塊
					try
					{
						Thread.sleep(10);
					}
					catch (InterruptedException e)
					{
					}
					System.out.println(Thread.currentThread().getName()+"....sale:"+tickets --);
				}else
				{
					break;
				}
			}
		}
	}
}
class ThreadDemo3 
{
	public static void main(String[] args) 
	{
		Ticket t=new Ticket();
		Thread t1=new Thread(t);
		Thread t2=new Thread(t);
		Thread t3=new Thread(t);
		Thread t4=new Thread(t);

		/*自定義線程名稱的方式:
		Thread t1=new Thread(t,"視窗1");
		Thread t2=new Thread(t,"視窗2");
		Thread t3=new Thread(t,"視窗3");
		Thread t4=new Thread(t,"視窗4");
		
		部分結果:
		視窗1....sale:44
		視窗1....sale:43
		視窗1....sale:42
		視窗1....sale:41
		視窗1....sale:40
		視窗4....sale:80
		視窗2....sale:81
		視窗2....sale:37
		視窗2....sale:36
		視窗2....sale:35
		視窗2....sale:34
		視窗2....sale:33
		視窗2....sale:32
		視窗2....sale:31
		*/

		t1.start();
		t2.start();
		t3.start();
		t4.start();

	}
}
           

【6】同步的兩種表現形式:

一種是代碼塊同步,一種是函數同步:

/*
練習:
銀行有一個小金庫,它有兩個顧客,每個顧客向金庫中各存300元錢
,分三次存入,一次是存入100元


【注意】
分析:哪裡出現了線程的安全問題
如何去找原因:
1.明确哪些代碼是多線程運作的代碼:
2.明确哪裡是共享的資料
3.明确多線程的運作代碼中哪裡涉及到了要執行共享的資料


*/


/*
//定義銀行類
class Bank
{
	//定義金庫
	private int sum=0;

	Object obj=new Object(); //定義了一個萬能的上帝對象,去實作同步代碼塊

	//定義銀行中的存錢方式
	public void add(int n)
	{
		synchronized(obj)
		{
			sum += n; //相當于sum=sum+n;(1)
			try
			{
				Thread.sleep(10);
			}
			catch (InterruptedException i)
			{
			}
			System.out.println(Thread.currentThread().getName()+"sum="+sum);//(2)

			//綜上所述的分析方法:得知(1)和(2)是多線程中都要執行的共享資料
			//是以對此代碼塊必須實作同步,上面的sleep方法是模拟了出問題的位置
		}
		
	}
}
*/

/*
看上述的代碼塊知道:其實作同步的代碼其實就是銀行中的add方法代碼主體
是以既然定義了同步該方法主體的同步,同理也可以定義add方法函數的同步、
是以便有了下列的定義函數的同步的方法,很簡單易行
*/
class Bank
{
	private int sum=0;
	public synchronized void add(int n)
	{
			sum += n;
			try
			{
				Thread.sleep(10);
			}
			catch (InterruptedException i)
			{
			}
			System.out.println(Thread.currentThread().getName()+": sum="+sum);
	}
}

class Cus implements Runnable
{
	private Bank b=new Bank();
	public void run()
	{
		for (int i=0;i<3;i++ )
		{
			b.add(100);
		}
		//為什麼不在for循環上加同步,主要考慮到加了同步,意思就是要等到進來的某一線程(即顧客)
		//依次存完之後,才能讓其他的顧客存錢,那樣的話就是有序的了
	}
}

class SynchronizedDemo4 
{
	public static void main(String[] args) 
	{
		Cus c=new Cus();
		Thread c1=new Thread(c);
		Thread c2=new Thread(c);
		c1.start();
		c2.start();

		/*結果
		Thread-0: sum=100
		Thread-0: sum=200
		Thread-0: sum=300
		Thread-1: sum=400
		Thread-1: sum=500
		Thread-1: sum=600
		*/
	}
}
           

【7】懶漢式單例模式具體分析:

/*
寫兩種方式的單例模式:注意其差別
*/

/*
	餓漢式的單例模式

class Single
{
	private static final Single s=new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}

}

*/

/*
	懶漢式的單例模式(延遲加載)
*/
class Single 
{
	private static Single s=null;
	private Single(){}
	//1.為了解決這種安全問題,可以同步函數的方法解決,但是每個線程進來都必須判斷鎖,效率比較低
	//private static synchronized Single getInstance()

	//2.為了提高效率,可以用同步代碼塊的方法解決
	public static Single getInstance()
	{
		if (s==null) //隻要有一個線程執行成功,那麼以後進來的線程就不再判斷鎖了,
		//因為s已經不為null了,直接不能建立單例的對象了,直接用就行了
		{
			synchronized(Single.class)
			{
				if (s==null)
				{
					//--0線程
					//--1線程
					//當0線程醒了,會建立一個對象
					//當1線程醒了,也會建立一個對象
					//……這就是出現了安全問題,不是單例模式了
					s=new Single();
					System.out.println(Thread.currentThread().getName()+"通路成功!");
				}
			}
		}	
		return s;
	}
}

class SingleDemo6 
{
	public static void main(String[] args) 
	{
		Single.getInstance();//在main方法中通過單例類對外提供的getInstance方法通路
		//結果:main通路成功!
	}
}
           

【8】多線程的死鎖問題:

什麼是死鎖:兩個線程彼此之間搶鎖的情況,互不相讓,就産生死鎖

代碼分析:

/*
	自己寫一個死鎖程式
	用鎖的嵌套方式來模拟死鎖
	模拟了兩個線程彼此之間搶鎖的情況,互不相讓,就産生死鎖
*/

class TestDeaDLock implements Runnable
{
	private boolean flag;//設定flag是為了讓線程進入不一樣的鎖中去執行

	TestDeaDLock(boolean flag)//在Runnable子類初始化時就設定flag
	{
		this.flag=flag;
	}
	public void run()
	{
		if (flag)
		{
			synchronized(MyLock.m1)
			{
				System.out.println("true:----lock! m1 ");
				//示範:拿着鎖1的線程想進入具備鎖2的内容中去,必然要拿到鎖2才行
				//此時持有鎖2的線程可能會把鎖2給持着鎖1的線程,但是也可能互不想讓,導緻死鎖的産生
				synchronized(MyLock.m2)
				{
					System.out.println("true:----lock m2 !");

				}
			}
			
		}else
		{
			synchronized(MyLock.m2)
			{
				System.out.println("false:----lock m2 !");
				//示範:拿着鎖2的線程想進入具備鎖1的内容中去,必然要拿到鎖1才行
				synchronized(MyLock.m1)
				{
					System.out.println("false:----lock m1 !");

				}
			}
		}
	}
}

class MyLock //定義自己的鎖類,其中有兩種鎖
{
	static MyLock m1=new MyLock(); //設定鎖1
	static MyLock m2=new MyLock();//設定鎖2
}

class DeadLockDemo7 
{
	public static void main(String[] args) 
	{
		Thread t1=new Thread(new TestDeaDLock(true)); //一個設定為true
		Thread t2=new Thread(new TestDeaDLock(false));//一個設定為false,主要是為了能夠進入不一樣的鎖中執行
		
		t1.start();
		t2.start();	
			/*
			示範結果可能會出現:
			true:----lock! m1
			false:----lock m2 !
			*/

	}
}
           

【9】JDK1.5中線程的新特性:

我們都知道,線上程中有一個很重要的機制,那就是等待喚醒機制:例如代碼分析:

/*
線程的等待喚醒機制
代碼的優化
【優化的重點】
1.資源中:包含基本的屬性,存入,取出函數
2.存入類:直接調用資源的存入函數
3.取出類:直接調用資源的取出函數

這樣看起來非常符合邏輯,層次清楚,便于代碼的維護和修改等
*/
//定義了一個共同的資源
class Res
{
	private String name;
	private String sex;
	boolean flag=false;
	public synchronized void set(String name,String sex)
	{
		if (flag)
			try{wait();}catch(Exception e){}
		this.name=name;
		this.sex=sex;
		this.flag=true;
		this.notify();
	}

	public synchronized void out()
	{
		if (!flag)
			try{wait();}catch(Exception e){}
		System.out.println("name="+name+" ....... sex="+sex);
		this.flag=false;
		this.notify();
	}
}
//定義存入資源類
class Input implements Runnable
{
	Res r;
	//存入類在初始化時,就有資源
	Input(Res r)
	{
		this.r=r;
	}
	//覆寫run方法
	public void run()
	{	
		int id=0;
		
		while (true)
		{
			//加鎖為了保證線程的同步,隻能允許一個線程在其中操作直到完成位置
			if (id==0)
				r.set("crystal","man");
			else
				r.set("楊女士","女");
			id=(id+1)%2;
		}
	}
}
//定義取出資源類
class Output implements Runnable
{
	Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while (true)
		{
			r.out();
		}
	}
}
/*
	上述的存入資源中和取出資源中:共享的代碼就是存入操作和取出操作,一定要保證通過進行,
	否則,當一個線程進入時,存完一個之後,再進入存第二時(存完名稱時,挂了),這是取出資源的線程
	在運作,這時候就會把出現:r.name="楊女士"和r.sex="man";的情況
*/
class InputOutputDemo3
{
	public static void main(String[] args) 
	{
		/*
		Res r=new Res(); //資源執行個體化

		Input in=new Input(r); //把資源和Runnable子類相關聯
		Output out=new Output(r);

		Thread t1=new Thread(in);//定義了兩個不同線程,一個存入,一個取出
		Thread t2=new Thread(out);
		
		t1.start(); //開啟并執行線程
		t2.start();
		*/

		Res r=new Res();
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();

		/*result:
			…………
			…………
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			name=楊女士 ....... sex=女
			name=crystal ....... sex=man
			…………
			…………
		*/
	}
}
           

那麼在這裡面用到了同步鎖,在JDK1.5中,新特性出現了:

将同步synchronized替換成為了Lock操作

将Object中的wait,notify,notifyAll,替換成了Condition對象

該對象可以Lock鎖,進行擷取

在該例子中,實作了本方隻喚醒對方的操作

用這種新特性:程式會進行得非常完美,完全符合邏輯!不浪費資源

import java.util.concurrent.locks.*;

class Resource
{
	private String name;
	private int id=1;
	boolean flag=false;
	private Lock lock=new ReentrantLock();
	private Condition condition_pro=lock.newCondition();
	private Condition condition_con=lock.newCondition();

	//定義生産者的同步生産函數
	public void set(String name)
	{
		lock.lock(); //進來就獲得鎖進入
		try
		{
			while (this.flag)
				//condition.await(); //首次進來因為為false,是以直接執行下面的語句
				condition_pro.await();//讓自身的線程等待
			this.name=name+"....."+id++;
			System.out.println(Thread.currentThread().getName()+" Producer:----"+this.name);
			this.flag=true;
			//condition.signalAll();//喚醒線程池中所有的線程,為的是讓不要出現全部等待的狀況
			condition_con.signal();//不用喚醒所有了,隻需要喚醒線程池中的對方就行,是以不用All了
		}
		catch (Exception e)
		{
		}
		finally
		{
			lock.unlock(); //最終釋放鎖
		}	
	}
	
	//定義消費者的同步消費函數
	public void out()
	{
		lock.lock();
		try
		{   
			while(!(this.flag))
				//condition.await();
			//同理:
			condition_con.await(); //本方等待
			System.out.println(Thread.currentThread().getName()+" Consumer:"+name);
			this.flag=false;
			//condition.signalAll();
			condition_pro.signal(); //喚醒對方
		}
		catch (Exception e)
		{
		}
		finally
		{
			lock.unlock();
		}
		
	}
}

//定義生産者具備調用生産功能
class Producer implements Runnable
{	
	private Resource res;
	Producer(Resource res)
	{
		this.res=res;
	}
	public void run()
	{
		while (true)
		{
			res.set("汽車");
		}
		
	}
}
//定義消費者具備消費功能
class Consumer implements Runnable
{	
	private Resource res;
	Consumer(Resource res)
	{
		this.res=res;
	}
	public void run()
	{
		while (true)
		{
			res.out();
		}
	}
}

class ProducerConsumerDemo6
{
	public static void main(String[] args) 
	{
		//把商品資源執行個體化
		Resource res=new Resource();

		//定義了兩個生産者路線(線程)和兩個消費者路線(線程)
		//并且同時啟動這些線程和執行
		new Thread(new Producer(res)).start();
		new Thread(new Producer(res)).start();
		new Thread(new Consumer(res)).start();
		new Thread(new Consumer(res)).start();

		/*
		結果:
		………………
		Thread-1 Producer:----汽車.....3522
		Thread-2 Consumer:汽車.....3522
		Thread-0 Producer:----汽車.....3523
		Thread-3 Consumer:汽車.....3523
		Thread-1 Producer:----汽車.....3524
		Thread-2 Consumer:汽車.....3524
		Thread-0 Producer:----汽車.....3525
		Thread-3 Consumer:汽車.....3525
		Thread-1 Producer:----汽車.....3526
		Thread-2 Consumer:汽車.....3526
		Thread-0 Producer:----汽車.....3527
		Thread-3 Consumer:汽車.....3527
		Thread-1 Producer:----汽車.....3528
		Thread-2 Consumer:汽車.....3528
		Thread-0 Producer:----汽車.....3529
		Thread-3 Consumer:汽車.....3529
		Thread-1 Producer:----汽車.....3530
		………………
		*/
	}
}
           

【10】多線程之間的通信模式分析:

多線程之間的通信就是:多個線程對象去通路共享的資料空間,但是每個線程具體的功能是不一樣的,例如:有一個資源,一個線程是向其中存,另一個線程是往裡面取出……

/*
線程之間的通信:
*/
//定義了一個共同的資源
class Res
{
	String name;
	String sex;
}
//定義存入資源類
class Input implements Runnable
{
	Res r;
	//存入類在初始化時,就有資源
	Input(Res r)
	{
		this.r=r;
	}
	//覆寫run方法
	public void run()
	{	
		int id=0;
		while (true)
		{
			//加鎖為了保證線程的同步,隻能允許一個線程在其中操作直到完成位置
			synchronized(r)
			{
				if (id==0)
				{
					r.name="crystal";
					r.sex="man";
				}else
				{
					r.name="楊女士";
					r.sex="女";
				}
				id=(id+1)%2;
			}	
		}
	}
}
//定義取出資源類
class Output implements Runnable
{
	Res r;
	Output(Res r)
	{
		this.r=r;
	}
	public void run()
	{
		while (true)
		{
			//加鎖為了保證線程的同步
			//取出資源也是一樣的,必須保證同步,隻能有一個線程操作
			synchronized(r)
			{
				System.out.println(r.name+"....."+r.sex);
			}
		}
	}
}
/*
	上述的存入資源中和取出資源中:共享的代碼就是存入操作和取出操作,一定要保證通過進行,
	否則,當一個線程進入時,存完一個之後,再進入存第二時(存完名稱時,挂了),這是取出資源的線程
	在運作,這時候就會把出現:r.name="楊女士"和r.sex="man";的情況
*/
class InputOutputDemo1
{
	public static void main(String[] args) 
	{
		Res r=new Res(); //資源執行個體化

		Input in=new Input(r); //把資源和Runnable子類相關聯
		Output out=new Output(r);

		Thread t1=new Thread(in);//定義了兩個不同線程,一個存入,一個取出
		Thread t2=new Thread(out);
		
		t1.start(); //開啟并執行線程
		t2.start();
		/*result:
		………………
		………………
		楊女士.....女
		楊女士.....女
		楊女士.....女
		crystal.....man
		crystal.....man
		………………
		………………
		*/
	}
}
           

【11】停止線程的方法總結:

停止線程的方法:

由于stop方法已經過時了

那麼怎樣才能停止線程呢?

思想隻有一種,那就是讓run方法結束

一般開啟多線程的運作,運作代碼通常是循環結構

是以隻要控制好循環,就能讓run方法結束,也就是讓線程結束

讓線程停止的3中方法:

1.設定标記:主線程結束之前改變标記讓run方法結束

2.使用interrupt():當線程處于wait(必須在同步中進行)或者sleep中斷時(在當機的區域中),用此方法清除當機,使線程回到運作中,這時會抛出異常,就在異常中設定标記,結束run方法,讓線程不再進入去繼續等待

3.使用守護線程的方法,setDaemon(true):在【線程啟動之前】就設定為守護線程,主要是為了當主線程結束時,背景會自動結束被守護的線程

【其實我們看到的都是前台的線程,背景也有線程在運作,可以了解為背景依賴前台的關系,目前台結束了,背景線程也就over了,這就是守護線程的特點】

class StopThread implements Runnable
{	private boolean flag=true;
	
	//在同步中,當線程進入當機狀況時,就不會讀到标記了,線程就不會結束
	public synchronized void run()
	{
		while (flag)
		{
			try
			{
				wait(); //兩個線程依次進來都是在這裡等待了……沒法被喚醒
				//是以該線程沒有被停止掉,并且主線程中改變标記的方法,在這裡,線程也無法讀到了

				//在這個時候就要用到線程中的interrupt方法,主要是為了讓線程從當機狀态回到運作狀态上
				//但是在運作之後,依然會進入等待狀态!

				//當回到運作狀态的時候,就需要在抛出的異常中去處理标記,改變标記值就OK了,當看到标記為false的
				//時候兩個線程就無法進入該方法區了,這樣線程就結束了
			}
			catch (InterruptedException e)
			{
				System.out.println(Thread.currentThread().getName()+".....Exception");
				flag=false;
			}
			System.out.println(Thread.currentThread().getName()+" run .....");
		}
	}

	public void setFlag()
	{
		flag=false;
	}
}
class StopDemo7 
{
	public static void main(String[] args) 
	{
		StopThread st=new StopThread();
		Thread t1=new Thread(st);
		Thread t2=new Thread(st);

		//t1.setDaemon(true);//分别把t1,t2線程都設定為守護線程後再啟動
		//t2.setDaemon(true);

		t1.start();
		t2.start();
		int i=0;
		while (true)
		{
			if (i++ == 50)
			{
				st.setFlag(); //主線程中改變了标記,讓其run方法主體結束,線程必然也結束了

				//t1.interrupt(); 
				//t2.interrupt();
				//在主線程結束之前把處于等待的線程用interrupt()方法清除掉當機的線程,讓其回到運作狀态
				
				break;
			}
			System.out.println(Thread.currentThread().getName()+"  ....."+i);
		}
		/*結果,這裡是通過改變标記的方法,結束run方法體,讓線程無法進入,直到主線程結束
		,其他兩個線程也結束了
		…………
		main  .....41
		main  .....42
		Thread-0 run .....
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50
		Thread-1 run .....
		Thread-0 run .....
		*/

		/*
		運用了中斷線程,清除了當機的方法之後:兩個線程也結束了,這是在同步中的做法
		main  .....38
		main  .....39
		main  .....40
		main  .....41
		main  .....42
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50
		Thread-0.....Exception
		Thread-0 run .....
		Thread-1.....Exception
		Thread-1 run .....
		*/

		/*
		設定為守護線程後運作的結果:不論是不是同步,主線程結束,背景線程就被Over了
		………………
		main  .....35
		main  .....36
		main  .....37
		main  .....38
		main  .....39
		main  .....40
		main  .....41
		main  .....42
		main  .....43
		main  .....44
		main  .....45
		main  .....46
		main  .....47
		main  .....48
		main  .....49
		main  .....50

		*/
	}
}
           

【12】join()和yield()方法的總結:

《1》首先join方法:

join():方法的作用

1.當線程A執行到了線程B的B.join()方法時,A就會釋放執行權給B,自己處于等待的當機狀态;

2.當線程B都執行完之後,線程A才能從當機狀态回到運作狀态去執行;

3.是以join可以用來臨時加入線程執行内容!

了解:

線程A把執行權釋放了,讓線程B去執行,自己當機,這時線程B如果被等待了,線程A也沒法回到運作狀态

那麼,這時就要用到interrupt()方法,去中斷清除A的當機,進而回到運作狀态,當然也可以中斷線程A

隻是會受到傷害(抛出異常),在異常中處理就行了實際開發中:一般都是使用匿名内部類來完成的

class TestJoin implements Runnable
{
	public void run()
	{
		for (int i=0;i<80 ;i++ )
		{
			System.out.println(Thread.currentThread().getName()+".....run....."+i);
		}
		
	}
}

class JoinDemo8 
{
	public static void main(String[] args) 
	{
		TestJoin tj=new TestJoin();
		Thread t1=new Thread(tj);
		Thread t2=new Thread(tj);

		t1.start();

		//try{t1.join();}catch(Exception e){} 
		//執行的結果是:線程0全部把run函數執行完之後,主線程mian和線程1才交替執行
		//是以:join的功能是能夠臨時獲得主線程的執行權,此程式中,主線程main把執行權釋放給了t1,
		//自己處于了當機狀态,當t1線程結束之後,主線程才回到運作狀态和t2線程繼續交替執行
		
		t2.start();
		try{t1.join();}catch(Exception e){} 
		//如果t1.join()處于這裡的話:主線程仍然是把自己的執行權釋放給了t1,自己處于當機狀态,
		//但是t2也是存活的線程,這時候cpu會自動發放執行權給t1或者t2去交替執行!主線程就悲催了,隻有等到
		//t1執行完之後,才能拿到自己的執行權,從當機狀态回到運作狀态

		for (int i=0; i<90; i++)
		{
			System.out.println(Thread.currentThread().getName()+".....mian....."+i);
		}
	}
}
           

《2》yield()方法:

暫停目前正在執行的線程對象,并執行其他線程。

當線程A進入時,暫停目前正在執行的内容,釋放執行權,讓其他的線程進來執行,當其他的進來執行

之後,又重新獲得執行權,繼續執行,這樣一來,等同于被共享的線程執行内容是“交替執行的‘

class TestSetPriority implements Runnable
{
	int i=100;
	public void run()
	{
		for (int i=0; i<20 ; i++)
		{
			System.out.println(Thread.currentThread().toString()+"....run...."+i);
			Thread.yield();
			/*
			Thread[Thread-0,5,main]....run....0
			Thread[Thread-1,5,main]....run....0
			Thread[Thread-0,5,main]....run....1
			Thread[Thread-1,5,main]....run....1
			Thread[Thread-0,5,main]....run....2
			Thread[Thread-1,5,main]....run....2
			Thread[Thread-0,5,main]....run....3
			Thread[Thread-1,5,main]....run....3
			Thread[Thread-0,5,main]....run....4
			Thread[Thread-1,5,main]....run....4
			Thread[Thread-0,5,main]....run....5
			Thread[Thread-1,5,main]....run....5
			*/
		}
	}
}

class SetPriorityDemo9 
{
	public static void main(String[] args) 
	{
		TestSetPriority sd=new TestSetPriority();
		Thread t1=new Thread(sd);
		Thread t2=new Thread(sd);
		t1.start();
		t2.start();

		/*
		for (int i=0; i<20; i++)
		{
			System.out.println(Thread.currentThread().toString()+"....mians....");
			/*
			Thread[main,5,main]....mian....
			Thread[main,5,main]....mian....
			Thread[main,5,main]....mian....
			
		}
		*/
		/*結果://這裡是toString()方法,該線程類覆寫了Object的這個方法
		在其中封裝了進了線程名稱,線程優先級,線程組
		…………………………
		Thread[Thread-0,5,main]....run.... 
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		Thread[Thread-0,5,main]....run....
		Thread[Thread-1,5,main]....run....
		………………………………
		*/

	}
}
           

【13】開發過程中為了提高效率,怎樣單獨封裝?

實際開發中:一般都是使用匿名内部類來完成的 用法:在獨立運算中:互相不相幹擾的時候,可以單獨封裝一下,提高了執行效率 下面就是三個線程同時執行,很高效!

class StandardThreadDemo10 
{
	public static void main(String[] args) 
	{

		//相當于繼承的方法
		new Thread(){
			public void run()
			{
				for (int i=0; i<5 ;i++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+i);
				}
			}
		}.start();


		for (int i=0;i<6;i++ )
		{
			System.out.println(Thread.currentThread().getName()+"...."+i);
		}

		//相當于實作接口的方法
		Runnable r=new Runnable()
		{
			public void run()
			{
				for (int i=0; i<10 ;i++ )
				{
					System.out.println(Thread.currentThread().getName()+"...."+i);
				}
			}
		};
		new Thread(r).start();

		/*
		main....0
		Thread-0....0
		main....1
		Thread-0....1
		main....2
		Thread-0....2
		main....3
		Thread-0....3
		main....4
		Thread-0....4
		main....5
		Thread-1....0
		Thread-1....1
		Thread-1....2
		Thread-1....3
		Thread-1....4
		Thread-1....5
		Thread-1....6
		Thread-1....7
		Thread-1....8
		Thread-1....9
		*/
	}
}
           

繼續閱讀