1:數據安全問題
1.1:什么情況下會出現數據安全問題?
多個線程對同一個資源進行操作,并且操作資源的語句有多條。那么這個時候這些語句因為cpu的隨機性,有可能被多個線程分開執行。導致數據安全問題。
例子:有3個人分別是你爸、你媽媽、你姐,去你的一個賬戶匯錢給你,每一個只能存3次,一次只能存100元。每存一次,請打顯示出賬戶里的余額。代碼體現:

1 public class SaveMoneyDemo1 { 2 3 public static void main(String[] args) { 4 SaveDemo1 s = new SaveDemo1(); 5 Thread t1 = new Thread(s); 6 t1.setName("老爸"); 7 Thread t2 = new Thread(s); 8 t2.setName("老媽"); 9 Thread t3 = new Thread(s);10 t3.setName("姐姐");11 t1.start();12 t2.start();13 t3.start();14 }15 16 }17 18 class SaveDemo1 implements Runnable{19 PRivate int sum = 0;20 //要執行的代碼塊放在run方法里面。21 public void run() {22 //每個人能存三次,就是循環三遍23 for(int i=0; i<3; i++){24 sum+=100;25 System.out.println(Thread.currentThread().getName()+"給你匯了100,目前賬號共有 "+sum+" 元");26 27 }28 }29 }30 /**31 * 執行結果:32 老媽給你匯了100,目前賬號共有 200 元33 老媽給你匯了100,目前賬號共有 400 元34 老媽給你匯了100,目前賬號共有 500 元35 姐姐給你匯了100,目前賬號共有 300 元36 姐姐給你匯了100,目前賬號共有 600 元37 姐姐給你匯了100,目前賬號共有 700 元38 老爸給你匯了100,目前賬號共有 200 元39 老爸給你匯了100,目前賬號共有 800 元40 老爸給你匯了100,目前賬號共有 900 元41 * 42 */沒有同步的匯款代碼運行結果好喜感。為什么會出現這種情況?分析:這三人存款是不需按照順序和次數的,反正幫你存夠三次就行了,所以用多線程更為合理??墒?,打個比方:當你媽媽在存錢的時候錢是存進去了,在沒來得及顯示余額的時候你爸正好也把錢存了進去,這時候總金額連同你媽和你爸的加在一起了!所以顯示出的金額會發生這樣的情況。那么如何解決類似的情況呢?這就要限定一個人存一次就先把金額加上,不能讓多人存完之后再一起加,如果這樣那金額的顯示就亂套了。這時候就要使用同步機制了。
1.2:解決方案: 同步機制
1.2.1:同步代碼塊。
synchronized(鎖){//鎖可以為任意對象。但是需要保證多個線程用的是同一把鎖。
對同一個資源的操作語句。
}
1.2.2:同步方法的鎖:
2.1:同步方法-----this
2.2:靜態同步方法-----字節碼文件對象。類名.class

1 public class SaveMoneyDemo2 { 2 3 public static void main(String[] args) { 4 SaveDemo2 s = new SaveDemo2(); 5 Thread t1 = new Thread(s); 6 t1.setName("老爸"); 7 Thread t2 = new Thread(s); 8 t2.setName("老媽"); 9 Thread t3 = new Thread(s);10 t3.setName("姐姐");11 t1.start();12 t2.start();13 t3.start();14 }15 16 }17 18 class SaveDemo2 implements Runnable{19 private int sum = 0;20 //要執行的代碼塊放在run方法里面。21 public void run() {22 //每個人能存三次,就是循環三遍23 synchronized(this){24 for(int i=0; i<3; i++){25 sum+=100;26 System.out.println(Thread.currentThread().getName()+"給你匯了100,目前賬號共有 "+sum+" 元");27 }28 }29 }30 }使用同步的數據安全的匯款1.3:如果加了同步,還出現數據安全問題,如何排查?
1.3.1:是否為同一把鎖
1.3.2:訪問資源的多條語句是否在同步中。
1.4:關于同步的拙劣理解:一件事先一口氣做完!不讓別人插手。(好像太牽強了)
2:死鎖問題——互不釋放資源(互相等待資源)
2.1 需求:用程序來描述以下情況:一手交錢一手交貨。商家與顧客兩人都是很小氣的人,顧客買商家的東西,商家收顧客的前,顧客說:先給我貨我再給你錢;商家說:先給我錢我再給你貨。最好的結局就是各自得到各自的東西。
2.2 分析:對于商家和客戶來說和他們倆有關的不是錢就是貨,而限制這兩人的也就是錢和貨。這樣一來錢和貨就可以看做是程序中的兩個鎖了。造成死鎖的原因:同步代碼嵌套!在平時開發時應避免同步嵌套!

1 public class DeadLockDemo1 { 2 public static void main(String[] args) { 3 Thread t1 = new Customer1(); 4 Thread t2 = new Seller1(); 5 t1.start(); 6 t2.start(); 7 } 8 } 9 10 class Customer1 extends Thread{11 public static Object money = new Object();12 @Override13 public void run() {14 //客戶有錢15 synchronized(money){16 System.out.println("客戶等商家給貨");17 //客戶等貨18 synchronized (Seller1.goods) {19 System.out.println("客戶給商家錢");20 }21 }22 }23 }24 25 class Seller1 extends Thread{26 public static Object goods = new Object();27 @Override28 public void run() {29 //商家有貨30 synchronized (goods) {31 System.out.println("商家等客戶給錢");32 //商家等錢33 synchronized (Customer1.money) {34 System.out.println("商家給客戶貨");35 }36 }37 }38 }39 40 /**41 * 如果想結果暴露地更明顯,可以使用sleep()方法42 * 運行死鎖的結果:43 * 客戶等商家給貨44 * 商家等客戶給錢45 * 46 */死鎖示例代碼3:等待喚醒機制。
前面多個線程案例中,每個線程執行的操作是一樣的。如果線程所執行的操作不一樣呢?比如一個線程負責生產產品,另外一個線程負責消費產品。
3.1:創建2個線程,2個線程的動作是不一樣。比如說:一個生產者和一個消費者。
需求:生產者生產一個產品。消費者消費一個產品。這就涉及到了等待喚醒機制。當生產者生產一個產品后進入等待模式等待消費者來消費這個產品,當消費者消費了這個產品,發現沒有產品了,消費者等待,叫生產者生產產品。生產者生產了產品則通知消費者。這就涉及到等待喚醒機制。
3.2:等待喚醒機制。
等待喚醒機制必須是在同步中進行,因為等待和喚醒都是要通過鎖來操作的,查看API就是的,wait()和notify()是屬于Object的方法,任何對象都是可以作為鎖的。
wait:讓當前線程等待。在哪里等待的就在哪里醒過來。
notify() :喚醒其中一個等待的線程。
notifyAll():喚醒所有等待的線程
wait和sleep的區別:
1:sleep會擁有鎖,而wait會釋放鎖。
2:sleep睡眠的時間是固定的,而wait等待的時間是不固定的。
3:sleep可以放在同步中,也可以不放在同步中。wait方法必須放在同步中。
3.3:一個生產者和一個消費者的代碼實現

1 /* 2 * 生產者生產一個產品。 3 * 消費者消費一個產品。 4 * 生產者可以生產多個產品,但是一次只能生產一個產品。消費了才能生產。 5 * 消費者可以消費多個產品。但是一次只能消費一個 產品。生產有了產品才能消費。 6 */ 7 public class ProCusDemo1 { 8 public static Object lock = new Object();//創建一個對象作為鎖 9 public static int num = 0;//產品數10 public static void main(String[] args) {11 Pro pro = new Pro();12 Cus cus = new Cus();13 pro.start();14 cus.start();15 }16 17 }18 19 class Pro extends Thread {20 @Override21 public void run() {22 //不斷生產,使用循環23 while(true){24 try {25 Thread.sleep(100);26 } catch (InterruptedException e1) {27 // TODO Auto-generated catch block28 e1.printStackTrace();29 }30 // System.out.println("111");31 //操作同一數據——產品數(num),使用同步代碼塊,也可以是等待喚醒機制必須在同步在同步中進行32 synchronized (ProCusDemo1.lock) {33 //當有一個產品了,生產者就不用生產了34 if(ProCusDemo1.num == 1){35 try {36 //不用生產的體現就是等待37 ProCusDemo1.lock.wait();38 // System.out.println("222");39 } catch (InterruptedException e) {40 e.printStackTrace();41 }42 }43 ProCusDemo1.num ++;//生產了一個產品44 System.out.println("生產者生產了一個產品,現有:"+ProCusDemo1.num+" 個");45 //當生產了一個產品之后就可以喚醒消費者消費了46 ProCusDemo1.lock.notify();47 }48 }49 }50 }51 52 class Cus extends Thread {53 @Override54 public void run() {55 while(true){56 // System.out.println("333");57 try {58 Thread.sleep(100);59 } catch (InterruptedException e1) {60 // TODO Auto-generated catch block61 e1.printStackTrace();62 }63 //多個線程操作同一數據使用同步64 synchronized (ProCusDemo1.lock) {65 if(ProCusDemo1.num == 0){66 try {67 ProCusDemo1.lock.wait();68 // System.out.println("444");69 } catch (InterruptedException e) {70 e.printStackTrace();71 }72 }73 ProCusDemo1.num--;74 System.out.println("消費者消費了一個產品,現有:"+ProCusDemo1.num+" 個");75 ProCusDemo1.lock.notify();76 }77 }78 79 }80 }等待喚醒機制——生產消費新聞熱點
疑難解答