實現Runnable解決多執行緒資料安全問題
xl_echo編輯整理,歡迎轉載,轉載請宣告文章來源。更多IT、程式設計案例、資料請聯絡QQ:1280023003,加群298140694。百戰不敗,依不自稱常勝,百敗不頹,依能奮力前行。——這才是真正的堪稱強大!!
之前的文章我們講到了,四個電影院視窗同時出售50張彩票的問題。在實現的過程中,我們使用Tread繼承,達到了需求的效果,但是也提出了一部分問題。這裡我們先使用Runnable進行改造之前的程式,實現效果,然後在來闡述之前的問題。
實現Tread完成需求程式碼如下:
package com.example.mybatisplusdemo.test;
/**
* @Author xl_echo
* @Date 2018/8/7 下午1:54
**/
public class Window extends Thread {
//售票視窗
private final String name;
//有50張電影票
private static final int MAXTicket = 10000;
public Window(String name) {
this.name = name;
}
private static int index = 1;
@Override
public void run(){
while (index <= MAXTicket){
System.out.println("視窗" + name + "當前是第" + (index++) + "張票");
}
}
public static void main(String[] args) {
Window window1= new Window("1號視窗");
window1.start();
Window window2= new Window("2號視窗");
window2.start();
Window window3= new Window("3號視窗");
window3.start();
Window window4= new Window("4號視窗" );
window4.start();
}
}
使用Runnable改造,改造後代碼如下
package com.example.mybatisplusdemo.test;
/**
* @Author xl_echo
* @Date 2018/8/8 上午10:53
**/
public class Test implements Runnable {
private int index = 1;
private final static int MAX = 50;
@Override
public void run() {
while (index <= MAX){
System.out.println("視窗" + Thread.currentThread() + "當前是第" + (index++) + "張票");
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args){
final Test test = new Test();
Thread windows1 = new Thread(test, "一號視窗");
Thread windows2 = new Thread(test, "二號視窗");
Thread windows3 = new Thread(test, "三號視窗");
Thread windows4 = new Thread(test, "四號視窗");
windows1.start();
windows2.start();
windows3.start();
windows4.start();
}
}
輸出結果這裡就不展示了,因為這裡的結果和之前程式的結果是一樣的,如果index增大的時候一樣會出現執行緒安全問題。
可能有朋友會發現我們沒有使用static修飾index,會不會是沒有修飾造成的安全問題,其實不然。比如,有一個執行緒拿到了index,這個時候準備執行方法,剛好另外一個執行緒也拿到了index,同時也準備執行方法,這個時候就會造成兩個視窗賣了同一張票。2而我們的輸出就出現了一下圖片中的輸出相同票數的情況。當我們的某一個執行緒拿到了最後一張票的時候,index=50了,剛好該執行緒進入了休眠。然後其他兩個執行緒進入,也獲取到了index發現並大於max,這個時候就會執行賣票,當這邊後進入的程式執行完成,前面的程式再次喚醒執行,就會出現賣了51張票。
解決辦法:使用synchronized關鍵字。
- 但是這個關鍵字加在什麼地方呢?
比如:加在run()方法上面,你會發現基本都是一號視窗買完了所有的票。因為同步鎖在第一條執行緒進入後,就鎖死了該方法的執行,必須要第一條執行緒執行完成該方法才能夠進入第二執行緒,所以等第二條進入的時候就會發現index已經大於50了,自然輸出結果就是一條執行緒執行完了所有出票。
public synchronized void run() {...}
比如:加在while迴圈裡面,使用synchronized(this){...}
括上方法裡面的所有程式碼,這個時候你會發現,基本每次都多出了三張票。因為:while判斷的下一步有四條執行緒執行,到最後一張票的時候,所有執行緒都拿到了,並且由於鎖的關係,每條執行緒都執行一次,所以這個時候就多賣了三張。
while (index <= MAX) {
synchronized (this) {
...
}
}
以上程式最終的解決辦法:
將同步鎖加在休眠上面,我們會發現我們效果達到了,執行緒安全問題解決了
synchronized (this) {
Thread.sleep(100);
}
這是什麼原因呢?
因為我們加在Thread.sleep(100);
上面的時候,我們的四個執行緒只有有一個進入休眠,其他的就會進入等待,剛好這時候進入休眠等待的執行緒就攔截了後的執行緒進行下一步操作。所以當我們的執行緒進入休眠的時候,前面的index疊加就已經完成了。
當我們的index疊加到46的時候,執行輸出和index再次疊加,剛好每條執行緒疊加一次,執行緒進入休眠,後面的想再次疊加也會被攔截。當最前面的執行緒喚醒之後,後面的執行緒都已經執行完成了,index自然是要比MAX大。
當然這裡也提出一個疑問?大家可以一起思考
那就是index疊加到46的時候,再次迴圈,第一條執行緒將index疊加為47,進入休眠,後面的執行緒在休眠時間內還沒有完成最後的三張票出售。這個時候就會不會出現資料安全問題?歡迎大家跟我聯絡。