目錄
1.多線程運行的安全問題
2.多線程同步代碼塊
3.同步方法的鎖是this
4.靜態同步方法的鎖是Class對象
5.單例設計模式(面試中的考點)
6.死鎖(一個發生死鎖的例子)
多線程運行的安全問題
例子:售票系統
1 class Ticket implements Runnable 2 { 3 //定義靜態變量ticket 4 PRivate static int ticket=100; 5 public void run() 6 { 7 while(true) 8 { 9 //判斷票編號是否大于010 if(ticket>0)11 {12 try13 {14 Thread.sleep(10);15 }16 catch (InterruptedException ie)17 {18 System.err.println("Error:"+ie);19 }20 System.out.println(Thread.currentThread().getName()+"--sale:"+ticket--);21 }22 }23 }24 25 }26 public class ThreadDemo27 {28 29 public static void main(String[] args)30 {31 //創建一個實現了Runnable接口的Ticket對象32 Ticket t=new Ticket();33 //創建4個線程34 Thread t1=new Thread(t);35 Thread t2=new Thread(t);36 Thread t3=new Thread(t);37 Thread t4=new Thread(t);38 //分別啟動4個線程39 t1.start();40 t2.start();41 t3.start();42 t4.start();43 44 }45 }
運行:
最后,打印出了了“0,-1,-2”的錯票情況(票的編號從1-100).多線程出現安全問題。
問題的原因:
當多條語句在操作用一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒執行完,
另一個數據參與進來執行,造成共享數據的錯誤。
解決方法:
對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中不允許其他線程參與進來。
java對于多線程的同步提供了專業的解決方法,就是同步代碼塊。
多線程同步代碼塊
synchronized(對象)
{需要被同步的代碼塊}
對象如同鎖,持有鎖的線程可以在同步代碼塊中執行,
沒有持有鎖的線程即使獲得CPU的執行權也進不去,因為沒有獲取鎖。
例子:火車上的衛生間。
同步的前提:
1.必須要有兩個或者兩個以上的線程。
2.必須是多個線程使用同一把鎖。
好處:解決了多線程的安全問題。
弊端:需要判斷鎖,較為消耗資源。
一般不可以把run()方法全放在同步代碼塊中,否則就是單線程了。
同步方法:
銀行
有兩個儲戶,分別存300元,每次存100元,存3次
目的:該線程是否有安全問題,如果有,如何解決?
如何找問題:
1.明確哪些代碼是多線程運行代碼
2.明確共享數據
3.明確多線程運行代碼中哪些代碼操作共享數據的。
1 class Bank 2 { 3 //定義sum,代表銀行的總金額 4 private int sum; 5 Object obj=new Object(); 6 public void add(int n) 7 { 8 9 //sum為共享數據,對sum有兩句操作,10 //防止出現不安全問題,使用同步代碼塊11 synchronized(obj)12 {13 sum=sum+n;14 try15 {16 Thread.sleep(10);17 }18 catch (InterruptedException ie)19 {20 System.err.println("Error:"+ie);21 }22 System.out.println("sum="+sum);23 }24 }25 }26 class Cus implements Runnable27 {28 private Bank b=new Bank();29 //存三次30 public void run()31 {32 for(int i=0;i<3;i++)33 b.add(100);34 }35 }36 public class BankDemo37 {38 public static void main(String[] args)39 {40 Cus cus=new Cus();41 //創建兩個線程,代表2個儲戶的存錢過程42 Thread t1=new Thread(cus);43 Thread t2=new Thread(cus);44 t1.start();45 t2.start();46 }47 }
注:同步的兩種表現形式:a.同步代碼塊 b.同步函數
同步方法的鎖是this
同步方法用的是哪一個鎖呢?
方法需要被對象調用,那么方法都有一個所屬對象的引用,就是this。
所以同步方法的鎖就是this。
靜態同步方法的鎖是Class對象
當同步方法被靜態修飾后,使用的鎖就不是this了,因為靜態方法中不可以使用this。
靜態方法進內存時,內存沒有本類對象,但一定有該類對應的字節碼文件對象。
類名.class。該對象的類型是Class。
靜態同步方法,使用的鎖是該方法所在的類的字節碼文件對象。類名.class
單例設計模式
1.餓漢式
1 class Single2 {3 private static final Single s=new Single();4 private Single(){}5 public static Single getInstance()6 {7 return s;8 }9 }
2.懶漢式:
1 /* 2 面試時的考點: 3 4 懶漢式的特點:實例的延遲加載 5 會出現的問題:多線程訪問時會出現安全問題 6 解決方法:用同步方法或者同步代碼塊都行, 7 但是有些低效,可以通過雙重判斷,減少判斷鎖的次數,稍微提高效率。 8 加同步時候:使用的鎖是該類的字節碼對象 9 10 */11 class Single12 {13 private static Single s=null;14 private Single(){}15 public static Single getInstance()16 {17 //通過雙重判斷,減少判斷鎖的次數,稍微提高效率。18 if (s==null)19 {20 synchronized (Single.class)21 {22 if(s==null)23 s=new Single();24 }25 }26 return s;27 }28 }
死鎖:同步中嵌套同步
死鎖的例子
1 class Test implements Runnable 2 { 3 private boolean flag; 4 Test(boolean _flag) 5 { 6 flag=_flag; 7 } 8 public void run() 9 {10 if(flag)11 {12 synchronized(MyLock.locka)13 {14 System.out.println("if locka");15 synchronized(MyLock.lockb)16 {17 System.out.println("if lockb");18 19 }20 21 }22 23 }24 else25 {26 synchronized(MyLock.lockb)27 {28 System.out.println("else lockb");29 synchronized(MyLock.locka)30 {31 System.out.println("else locka");32 33 }34 35 }36 37 }38 39 }40 }41 class MyLock42 {43 static Object locka=new Object();44 static Object lockb=new Object();45 }46 public class DeadLockTest47 {48 public static void main(String[] args)49 {50 Thread t1=new Thread(new Test(true));51 Thread t2=new Thread(new Test(false));52 t1.start();53 t2.start();54 55 }56 57 }
|
新聞熱點
疑難解答