1. 程式人生 > >一個指令重排序引發的chaos

一個指令重排序引發的chaos

先貼出正確的程式碼:

 1 package com.xiaobai.thread.main;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 
 5 @Slf4j
 6 public class ThreadMain {
 7 
 8     private volatile static int ticket;
 9 
10     private static class RunningTask implements Runnable{
11 
12         public RunningTask(boolean proOrSell) {
13 this.proOrSell = proOrSell; 14 } 15 16 private boolean proOrSell = true;//如果為靜態,會被例項共享 17 18 public void run() { 19 while (true) {//只同步需要同步的程式碼,while(true)不能放在同步裡面!!否則除非執行緒自己wait(),永遠不會輪到其他執行緒!! 20 synchronized (RunningTask.class) { 21 proOrSell();
22 } 23 } 24 } 25 26 private void proOrSell() { 27 if(proOrSell) { 28 ticket++; 29 log.info("Tickets/Pro:" + ticket); 30 RunningTask.class.notifyAll(); 31 if(ticket >= 1000) { 32 try
{ 33 System.out.println("--------------------------------------------------------------------------------------------------------------------------------------->Pro Wait!!!!!"); 34 RunningTask.class.wait();//持有鎖的本執行緒等待 35 } catch (InterruptedException e) { 36 37 } 38 } 39 }else { 40 System.out.println(Thread.currentThread().getName() + " take control!"); 41 if(ticket <= 100) { 42 try { 43 System.out.println(Thread.currentThread().getName() + " <=100!"); 44 System.out.println("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!"); 45 RunningTask.class.wait(); 46 } catch (InterruptedException e) { 47 48 } 49 }else { 50 ticket--; 51 log.info("Tickets------->Sell:" + ticket); 52 System.out.println(Thread.currentThread().getName() + " decrease tickets!"); 53 if(ticket <= 500) { 54 RunningTask.class.notifyAll(); 55 } 56 } 57 } 58 } 59 } 60 61 private static void proOrSell() { 62 Runnable task1 = new RunningTask(true); 63 Runnable task2 = new RunningTask(false); 64 Thread thread1 = new Thread(task1); 65 Thread thread2 = new Thread(task2); 66 Thread thread3 = new Thread(task2); 67 Thread thread4 = new Thread(task2); 68 thread1.start(); 69 try { 70 Thread.sleep(3000); 71 } catch (InterruptedException e) { 72 73 } 74 thread2.start(); 75 thread3.start(); 76 thread4.start(); 77 } 78 79 public static void main(String[] args) { 80 proOrSell(); 81 while (ticket <= 0) { 82 System.out.println("------------------------------------------------------->Ticket System Broken!!!!!!"); 83 System.exit(0); 84 } 85 } 86 87 }
View Code

程式碼簡單說明:

1.多執行緒生產者-消費者程式,一個生產者,3個消費者

2.同步共享變數操作程式碼,同一把鎖(內部巢狀類的位元組碼物件)

3.注意while(true)要在同步程式碼外面,否則沒有意義!

4.改造的等待-喚醒機制,控制票數在一個下限(100)和一個上限(1000)

 

正確程式碼執行結果摘錄:

1)

Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:105
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:104
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:103
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:102
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:101
Thread-1 decrease tickets!
Thread-1 take control!
2018-12-09 21:14:11.357 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:100
Thread-1 decrease tickets!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:101
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:102
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:103
2018-12-09 21:14:11.357 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:104

 

2)
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:107
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:106
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:105
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:104
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:103
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:102
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:101
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.375 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:100
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:101
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:102
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:103
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:104
2018-12-09 21:14:11.376 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:105

 

3)
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:104
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:103
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:102
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:101
Thread-2 decrease tickets!
Thread-2 take control!
2018-12-09 21:14:11.395 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:100
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:101
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:102
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:103
2018-12-09 21:14:11.395 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:104

 

說明:當票數達到下限100時,三個消費者先後偵測到並在同一個監視器上阻塞等待,由生產者繼續屯票,並喚醒消費者繼續消費。生產者偵測票數達到1000時暫停生產,阻塞等待。

 

錯誤的程式:將else中巢狀的else中的內容拿到外面,去掉巢狀else.也就是說,無論上面的if條件是否滿足,都執行現在在巢狀else中的全部內容。

錯誤程式出現的執行現象:

 

Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:113
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:112
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:111
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:110
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:109
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:108
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:107
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:106
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:105
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:104
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:103
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:102
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:101
Thread-3 decrease tickets!
Thread-3 take control!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:100
Thread-3 decrease tickets!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:99
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-1] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:98
Thread-1 decrease tickets!
Thread-1 take control!
Thread-1 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-2] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:97
Thread-2 decrease tickets!
Thread-2 take control!
Thread-2 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-3] INFO com.xiaobai.thread.main.ThreadMain -
Tickets------->Sell:96
Thread-3 decrease tickets!
Thread-3 take control!
Thread-3 <=100!
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==>Sell Wait!!!!
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:97
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:98
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:99
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:100
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:101
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:102
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:103
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:104
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:105
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:106
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:107
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:108
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:109
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:110
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:111
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:112
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:113
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:114
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:115
2018-12-09 20:53:18.891 [Thread-0] INFO com.xiaobai.thread.main.ThreadMain -
Tickets/Pro:116

 

分析:通過不同程式段列印和執行緒名列印可以清晰看到,消費者程式段中發生了指令重排序。if段後面的程式被挪到最前面執行,造成<=100也消費的情況。程式段中一致的執行緒名和上下文沒有不同執行緒名交叉出現的情況說明同步鎖正確且生效,是執行緒內順序程式段的指令重排序造成了混亂。將if下面的程式段放在巢狀的else中後,程式執行正常,不再出現<=100消費的情況。生產者這邊也沒有溢位1000的情況發生。

 

總結:多執行緒程式,在注意共享變數操作需要同步需要同步的程式碼的同時,還需要注意執行緒內指令重排序造成多執行緒環境出現混亂的情況,這種情況同樣會造成類似非執行緒安全的情況,但並非執行緒安全問題,而是同一個執行緒中的順序混亂被帶到多執行緒操作中,造成了混亂和不安全。