多執行緒-3
阿新 • • 發佈:2020-12-25
JAVA執行緒
執行緒的安全問題:
舉個栗子:
售貨員買票:三個售貨員同時買票,並且票數一定。
public static void main(String[] args) { Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable { //總票數 privateint count = 10; @Override public void run() { while (true) { if (count > 0) { //賣票 System.out.println("正在準備買票"); try { Thread.sleep(1000);//時間差,導致其他執行緒進入 } catch(InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName() + ":出票成功,剩餘:" + count); //打印出-1,-2 } else { break; } } } }
正在準備買票 正在準備買票 正在準備買票 Thread-0:出票成功,剩餘:7 正在準備買票 Thread-1:出票成功,剩餘:8 正在準備買票 Thread-2:出票成功,剩餘:8 正在準備買票 Thread-2:出票成功,剩餘:6 正在準備買票 Thread-0:出票成功,剩餘:4 正在準備買票 Thread-1:出票成功,剩餘:5 正在準備買票 Thread-1:出票成功,剩餘:3 正在準備買票 Thread-2:出票成功,剩餘:2 正在準備買票 Thread-0:出票成功,剩餘:2 正在準備買票 Thread-1:出票成功,剩餘:1 正在準備買票 Thread-2:出票成功,剩餘:0 Thread-0:出票成功,剩餘:-1 Thread-1:出票成功,剩餘:-2
出現負數的票數。這就是執行緒不安全的情況,執行緒休眠的情況導致其他執行緒會趁虛而入(搶佔式排程嘛)。
如何解決:1,使用同步程式碼塊
1 public static void main(String[] args) { 2 //執行緒不安全 3 //解決方案1:同步程式碼塊 4 /* 5 Object o = new Object(); 6 格式: synchronized(鎖物件){ 7 } 8 */ 9 Runnable run = new Ticket(); 10 new Thread(run).start(); 11 new Thread(run).start(); 12 new Thread(run).start(); 13 } 14 static class Ticket implements Runnable { 15 //總票數 16 private int count = 10; 17 private Object o = new Object();//同一把鎖,即公鎖 18 @Override 19 public void run() { 20 //Object o = new Object();//如果將鎖物件new在這裡,導致每個執行緒都有自己的鎖,這樣還是會發生執行緒不安全,因為每個執行緒只關注自己的鎖。注意!! 21 while (true) { 22 synchronized (o) { 23 if (count > 0) { 24 //賣票 25 System.out.println("正在準備買票"); 26 try { 27 Thread.sleep(1000);//時間差,導致其他執行緒進入 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 count--; 32 System.out.println(Thread.currentThread().getName() + ":出票成功,剩餘:" + count); 34 } else { 35 break; 36 } 37 } 38 } 39 } 40 }
2、同步方法
1 public static void main(String[] args) { 2 //執行緒不安全 3 //解決方案2:同步方法 4 5 Runnable run = new Ticket(); 6 new Thread(run).start(); 7 new Thread(run).start(); 8 new Thread(run).start(); 9 10 /*new Thread(new Ticket()).start(); 11 new Thread(new Ticket()).start(); 12 new Thread(new Ticket()).start(); 13 這樣相當於沒鎖,注意!!*/ 14 } 15 static class Ticket implements Runnable { 16 //總票數 17 private int count = 10; 18 //給方法新增synchronized 19 public synchronized boolean sale(){ 22 if (count > 0) { 23 //賣票 24 System.out.println("正在準備買票"); 25 try { 26 Thread.sleep(1000);//時間差,導致其他執行緒進入 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 count--; 31 System.out.println(Thread.currentThread().getName()+":出票成功,剩餘:" + count); 32 return true; 33 }else { 34 return false; 35 } 36 } 37 @Override 38 public void run() { 39 while (true) { 40 boolean flag = sale(); 41 if(!flag){ 42 break; 43 } 44 } 45 } 46 }
正在準備買票 Thread-0:出票成功,剩餘:9 正在準備買票 Thread-0:出票成功,剩餘:8 正在準備買票 Thread-0:出票成功,剩餘:7 正在準備買票 Thread-0:出票成功,剩餘:6 正在準備買票 Thread-0:出票成功,剩餘:5 正在準備買票 Thread-0:出票成功,剩餘:4 正在準備買票 Thread-0:出票成功,剩餘:3 正在準備買票 Thread-0:出票成功,剩餘:2 正在準備買票 Thread-0:出票成功,剩餘:1 正在準備買票 Thread-0:出票成功,剩餘:0 Process finished with exit code 0
兩種方法結果如上圖。
3、顯示鎖 Lock
1 public static void main(String[] args) { 2 //執行緒不安全 3 //解決方案3:顯示鎖 Lock 子類 ReentrantLock 4 Runnable run = new Ticket(); 5 new Thread(run).start(); 6 new Thread(run).start(); 7 new Thread(run).start(); 8 } 9 static class Ticket implements Runnable { 10 //總票數 11 private int count = 10; 12 //顯示鎖 :fair引數為true 就表示公平鎖 13 private Lock l = new ReentrantLock(true); 14 @Override 15 public void run() { 16 while (true) { 17 l.lock(); 18 if (count > 0) { 19 //賣票 20 System.out.println("正在準備買票"); 21 try { 22 Thread.sleep(1000);//時間差,導致其他執行緒進入 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 count--; 27 System.out.println(Thread.currentThread().getName()+":出票成功,剩餘:" + count); 28 }else { 29 break; 30 } 31 l.unlock(); 32 } 33 } 34 }
記得用完要解鎖unlock()。(公平鎖:先到先得,公平嘛)。