1. 程式人生 > >黑馬程式設計師--第十一天:多執行緒

黑馬程式設計師--第十一天:多執行緒

---------------------- ASP.Net+Android+IO開發S.Net培訓、期待與您交流! ----------------------

/*11-1
程序: 是一個正在執行中的程式。
		每一個程序執行都有一個執行順序,該順序是一個執行路徑,或者叫一個單元。

執行緒:就是程序中的一個獨立的控制單元。
		執行緒在控制著程序的執行。

一個程序中至少有一個執行緒。


java vm 啟動時會有一個程序java.exe

該程序中至少有一個執行緒負責java程式的執行。
而且這個執行緒執行的程式碼存在於main方法中。
該執行緒稱之為主執行緒

擴充套件:其實更細節的說明jvm,jvm啟動不止一個執行緒,還有負責垃圾回收機制的執行緒。
*/

class  
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}


/*11-2
如何在自定義的程式碼中,自定義一個執行緒?
	通過api查詢,java已經提供了對執行緒這類食物的描述。就是Thread。
建立執行緒的第一種方式:繼承Thread
步驟:
1.定義類,繼承Thread
2.複寫Thread的run方法
	目的:將自定義程式碼儲存在run方法,讓執行緒執行。
3.呼叫執行緒的start方法。
	該方法兩個作用:啟動執行緒,呼叫run方法。


發現執行結果每次都不同。
因為多個執行緒都獲取cpu的執行權。cpu執行到誰,誰就執行。
明確一點,在某一時刻,只能有一個程式在執行。(多核除外)
cpu 在做著快速切換,以達到看上去是同時執行的效果。
我們可以形象把多執行緒的執行行為在互相搶奪cpu的執行權。

這就是多執行緒的一個特性:隨機性。誰搶到,誰執行。至於執行多長,cpu說了算。
*/


class Demo extends Thread
{
	public void run(){
		for(int i = 0; i < 60; i++)
		System.out.println("hello");
	}
}

class  
{
	public static void main(String[] args) 
	{
		Demo d = new Demo;
		d.start();
		//d.run(); 
		for (int i=0;i<60 ;i++ )
		System.out.println("Hello World!");
	}
}


/*11-3
為什麼要覆蓋run方法?
Thread類用於描述執行緒
該類定義了一個功能,用於儲存執行緒要執行的程式碼,該功能就是run方法。

也就是說Thread類中的run方法,用於儲存執行緒要執行的程式碼。
*/


/* 11-6 獲取執行緒以及名稱
執行緒都有自己預設的名稱
Thread-編號 該編號從0開始。

static Thread CurrentThread();獲取當前執行緒物件。
getName(); 獲取執行緒名稱。
設定執行緒名稱:setName或者建構函式。
Thread.currentThread()=this;(this.getName());

*/

class  
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}


/*11-7
需求:簡單的賣票程式。
多個視窗賣票。
*/


/*11-8 實現Runnable介面
步驟:
1.定義類實現Runnable介面。
2.覆蓋Runnable介面中的run方法。
	將執行緒要執行的程式碼存放在該run方法中。
3.通過Thread類建立執行緒物件。
4.將Runnable介面的子類物件作為實際引數傳遞給Thread類的建構函式。
	為什麼要將Runnable介面的子類物件傳遞給Thread的建構函式?
	因為,自定義的run方法所屬的物件是Runnable介面的子類,所以要讓執行緒
	去指定物件的run方法。
5.呼叫Thread類的start方法開啟執行緒並呼叫Runnable介面子類的run方法。


實現方式和家長方式有什麼區別?

實現的好處:避免了單繼承的侷限性(可以繼承其他類,不只是Thread)
在定義執行緒是,建立使用實現方式。

兩種方式的區別:
繼承Thread: 執行緒程式碼存放Thread子類run方法中。
實現Runnable:執行緒程式碼存放介面子類的run方法。
*/


/*11-9 多執行緒安全問題
通過分析,發現打印出0,-1,-2等票
多執行緒 的執行出現安全問題。

*/
class Ticket  implements Runnable //extends Thread //因為Thread有實現Runnable介面,
//所以不實現Runnable介面也可以多個視窗賣同一組票,
//直接用extends Thread, new Thread(new Ticket(){覆蓋run方法}).start().
//區別在與實現Runnable可以繼承其他類
{
	private int tick = 100;
	public void run(){
		while (tick>0)
		{
			System.out.println(Thread.currentThread().getName()+"sale: " + tick--);
		}
	}
}

class  Demo
{
	public static void main(String[] args) 
	{
		Ticket t1 = new Ticket();
		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();

		new Thread(t1).start();
		new Thread(t1).start();
		new Thread(t1).start();
		//t1.start();
		//t2.start();
		//t3.start();
		//t1.run();
		//t2.run();
		//t3.run();
		System.out.println("Hello World!");
	}
}



/*11-9 多執行緒安全問題
通過分析,發現打印出0,-1,-2等票
多執行緒 的執行出現安全問題。

問題原因:
	當多條語句在操作同一個執行緒共享資料時,一個執行緒對多條語句只執行了一部分,還沒有執行完。
	另一個執行緒參與進來,導致共享資料的錯誤。

解決方法:
	對多條操作共享資料的語句,只能讓一個執行緒執行完。在執行過程中,其他執行緒不可以參與執行。

*/


/*11-10:同步程式碼塊
java對於多執行緒安全問題提供了專業的解決方式。
就是同步程式碼塊。
synchronized(物件){
	需要被同步的程式碼;
}

物件如同鎖,持有鎖的執行緒可以在同步中執行。
沒有持有鎖的執行緒即使獲取cpu的執行權也進不去,因為沒有獲取鎖。

火車上的衛生間


同步的前提:
1.必須要有2個或者2個以上的執行緒。
2.必須是多個執行緒使用同一個鎖。

必須保證同步中只有一個執行緒在執行。

好處:解決了多執行緒的安全問題。
弊端:多個執行緒需要判斷鎖,較為消耗資源。
*/

class Ticket  implements Runnable 
{
	private int tick = 100;
	public void run(){

		while (true)
		{
			synchronized(new Object()){
					if (tick>0)
					{
					Thread.sleep(10);
					System.out.println(Thread.currentThread().getName()+"sale: " + tick--);
					}else
						break;
			}
		}
	}
}

class  Demo
{
	public static void main(String[] args) 
	{
		Ticket t1 = new Ticket();
		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();

		new Thread(t1).start();
		new Thread(t1).start();
		new Thread(t1).start();
		//t1.start();
		//t2.start();
		//t3.start();
		//t1.run();
		//t2.run();
		//t3.run();
		System.out.println("Hello World!");
	}
}


/*11-11
需求:
銀行有一個金庫。
有兩個儲戶分別存300元,每次存100,存3次。

目的:該程式是否有安全問題,如果有,如何解決?

如何找問題:
1.明確哪些程式碼是多執行緒執行程式碼。
2.明確共享資料。
3.明確多執行緒執行程式碼中哪些語句是操作共享資料的?
*/


class Bank
{
	private int sum;
	public synchronized void add(int n){
		//synchronized(new Object()){
		sum = sum + n;
		try{Thread.sleep(10);}catch(Exception e){}
		System.out.println("Sum="+sum);
		//}
	}
}

class Cus implements Runnable
{
	private Bank b = new Bank();
	public void run(){
		for (int x=0;x<3 ;x++ )
		{
			b.add(100);
		}
	}
}

class  BankDemo
{
	public static void main(String[] args) 
	{
		Cus c = new Cus();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
		System.out.println("Hello World!");
	}
}



/*11-12
同步函式用的是哪一個鎖?
函式需要被物件定義。那麼函式都有一個所屬物件引用。就是this。
所以同步函式所用的鎖是this。

通過該程式進行驗證。
為了驗證方便,所用2個執行緒來賣票
一個執行緒在同步程式碼塊中
一個執行緒在同步函式中
都在執行賣票動作。
*/

/*11-13靜態同步函式的鎖是Class物件。
如果同步函式被靜態修飾後,使用的鎖是什麼呢?

通過驗證,發現不是this,因為靜態方法中也不可以定義this。

靜態進記憶體時,記憶體中沒有本類物件,但是一定有該類對應的位元組碼檔案物件。
類名.class 該物件的型別是Class。

靜態的同步方法,使用的鎖是該方法所在的類的位元組碼檔案物件。類名.class

*/

class Ticket  implements Runnable 
{
	boolean flag = true;
	private int tick = 100;
	Object obj = new Object();
	public void run(){

		if (flag)
		{
			while (true)
			{				//Ticket.class
				synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖
					if (tick>0)
					{
						try{Thread.sleep(10);}catch (Exception e){}
						System.out.println(Thread.currentThread().getName()+"sale: "+"t--" + tick--);
					}else
						break;
				}
			}
		}else{
				while(true){
					show();
					if(tick<=0) break;
				}
			}
	}

	synchronized void show(){//static
		if (tick>0)
			{
				try{Thread.sleep(10);}catch (Exception e){}
				System.out.println(Thread.currentThread().getName()+"sale: " +"s.."+ tick--);
			}
	}
}

class  Demo
{
	public static void main(String[] args) throws Exception
	{
		Ticket t1 = new Ticket();
		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();

		new Thread(t1).start();
		Thread.sleep(10);
		//t1.flag = false;
		new Thread(t1).start();
		new Thread(t1).start();
		//t1.start();
		//t2.start();
		//t3.start();
		//t1.run();
		//t2.run();
		//t3.run();
		System.out.println("Hello World!");
	}
}


/*11-14
單例設計模式。

*/

//餓漢式:
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(){}
	public synchronized static Single getInstance(){
		if (s == null)
			s = new Single();

		return s;
	}
}

class Single{
	private static Single s = null;
	private Single(){}
	public static Single getInstance(){

		if (s == null)
		{
			synchronized(Single.class){//該類所屬的位元組碼檔案物件
				if (s == null)
					//-->A
					//-->B
					s = new Single();
			}
		}

		return s;
	}
}
/*
懶漢式和餓漢式有什麼不同?
	懶漢式的特點在於例項的延遲載入。
	懶漢式延遲載入的問題:多執行緒訪問時存在安全問題。
怎麼解決?
	可以加同步來解決。用同步程式碼塊和同步函式都行。
		但是有些低效,用雙重判斷能解決效率問題
加同步是使用的鎖是哪一個?
	該類所屬的位元組碼檔案物件。
*/

class  
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}

/*11-15
死鎖:

*/

class Ticket  implements Runnable 
{
	boolean flag = true;
	private int tick = 100;
	Object obj = new Object();
	public void run(){

		if (flag)
		{
			while (true)
			{				//Ticket.class
				synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖
					if (tick>0)
					show();
					else
						break;
				}
			}
		}else{
				while(true){
					show();
					if(tick<=0) break;
				}
			}
	}

	synchronized void show(){//static
		synchronized(obj){//當該引數寫new Object時,每個執行緒進來都持有不同的物件鎖
			if (tick>0)
			{
				try{Thread.sleep(10);}catch (Exception e){}
				System.out.println(Thread.currentThread().getName()+"sale: "+"t--" + tick--);
			}
		}
	}
}

class  Demo
{
	public static void main(String[] args) throws Exception
	{
		Ticket t1 = new Ticket();
		//Ticket t2 = new Ticket();
		//Ticket t3 = new Ticket();

		new Thread(t1).start();
		Thread.sleep(10);
		//t1.flag = false;
		new Thread(t1).start();
		new Thread(t1).start();

		System.out.println("Hello World!");
	}
}



//-------------------------------------
class Test implements Runnable
{
	private boolean flag;
	Test(boolean flag){
		this.flag = flag;
	}

	public void run(){
		if (flag)
		{
			synchronized(MyLock.locka){
				System.out.println("if locka");
				synchronized(MyLock.lockb){
					System.out.println("if lockb");
				}
			}
		}else{
			synchronized(MyLock.lockb){
				System.out.println("else lockb");
				synchronized(MyLock.locka){
					System.out.println("else locka");
				}
			}
		}
	}
}

class MyLock
{
	static Object locka = new Object();
	static Object lockb = new Object();
}

class DeadLockTest
{
	public static void main(String[]args){
		Thread t1 = new Thread(new Test(true));
		Thread t2 = new Thread(new Test(false));
		t1.start();
		t2.start();
	}
}

//個人死鎖的例子----------------------------
public class Demo {
	 
	public static void main(String[] args) {

		Count c = new Count();
		System.out.println("start");
		new Thread(new CountA(c)).start();
		new Thread(new CountB(c)).start();
	}
}

class Count{ 
	int a = 0;
	int b = 0;
	
	public  int countA(){
		synchronized(Count.class){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
			
			System.out.println("countA counted");
			
			countC();
			return ++a;  
		}
	}
	
	public int countB(){
		synchronized(Count.class){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	

			countA();
			return ++b ;
		}
	}

	public  int countC(){
		synchronized(Count.class){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
			
			System.out.println("countA counted");
			
			return ++a;  
		}
	}
}

class CountA implements Runnable{
	
	Count c = null;
	CountA(Count c){
		this.c = c;
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i =0; i<10;i++){
			int num = c.countA() ;//+ c.countB();
			System.out.println("countA = "+num);
		}
	}
	
}

class CountB implements Runnable{
	
	Count c = null;
	CountB(Count c){
		this.c = c;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i =0; i<10;i++){
			int num = c.countB() ;//+ c.countA();
			System.out.println("countB = "+num);
		}
	}
	
}


---------------------- ASP.Net+Android+IO開發S.Net培訓、期待與您交流! ----------------------