淺談多執行緒安全問題
阿新 • • 發佈:2018-12-13
下面我們還是用看電影賣票的案例來談一談多執行緒安全的問題。
方案一:使用同步程式碼塊
//實現賣票案例 /* 賣票案例出現執行緒安全問題 解決方案一:使用同步程式碼塊 格式: synchronized(鎖物件){ 可能會出現執行緒安全問題的程式碼(訪問了共享資料的程式碼) } 注意: 1.通過程式碼塊中的鎖物件,可以使用任意的物件 2.但是必須保證多個執行緒使用的鎖物件是同一個 3.鎖物件作用: 把同步程式碼塊鎖住,只讓一個執行緒在同步程式碼塊中執行 */ /* 總結:同步中的執行緒,沒有執行完畢不會釋放鎖,同步外的執行緒沒有鎖進不去同步 同步保證了只能有一個執行緒在同步中執行共享資料,保證了安全 程式頻繁的判斷鎖,獲取鎖,釋放鎖,程式的效率會降低 */ public class RunnableImpl implements Runnable{ //定義一個多執行緒共享的票源 private int piao = 100; //建立一個鎖物件 Object obj = new Object(); @Override//設定任務買票 public void run() { while (true){//使用死迴圈讓買票操作重複著執行 //建立同步程式碼塊 synchronized (obj){ if(piao>0){//判斷票是否存在 //提高安全問題的出現概率,讓程式睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //票存在,買票,piao-- System.out.println(Thread.currentThread().getName()+"-->正在賣第"+piao+"張票"); piao--; } } } } }
測試類:
/* 1.單執行緒程式是不會出現執行緒安全問題的 2.多執行緒程式,沒有訪問共享資料,不會產生問題 3.多執行緒訪問了共享的資料,會產生執行緒安全問題 */ //模擬買票案例,建立三個執行緒,同時開啟,對共享的票進行出售 public class Test01 { public static void main(String[] args) { //建立Runnable介面的實現類物件 RunnableImpl run = new RunnableImpl(); //建立Thread類物件,構造方法中傳遞Runnable介面的實現類物件 Thread thread1 = new Thread(run); Thread thread2 = new Thread(run); Thread thread3 = new Thread(run); thread1.start();//開啟多執行緒 thread2.start(); thread3.start(); /* 出現了執行緒安全問題,賣票出現了重複的票和不存在的票 注意: 執行緒安全問題是不能產生的,我們可以讓一個執行緒訪問共享資料的時候,無論是否失去了cpu的執行權; 讓其他的執行緒只能等待,等待當前程式賣完票,其他執行緒在進行賣票 保證:使用一個執行緒在賣票 */ } }
方案二:使用同步方法
//實現賣票案例 /* 賣票案例出現了執行緒安全問題 解決執行緒安全問題方法二:使用同步方法 使用步驟: 1.把訪問了共享資料的程式碼抽取出來,放到一個方法中 2.在方法上新增synchronized修飾符 格式:定義方法的格式 修飾符 synchronized 返回值型別 方法名(引數列表){ 可能會出現執行緒問題的程式碼(訪問了共享資料的程式碼) } */ public class RunnableImpl implements Runnable { //定義一個多執行緒共享的票源 private int piao = 100; @Override//設定任務買票 public void run() { while (true) {//使用死迴圈讓買票操作重複著執行 method(); } } /* 同步方法也會把方法內部的程式碼鎖住,只讓一個執行緒執行 同步方法的鎖物件是誰? 就是實現類物件new RunnableImpl() 也就是this */ public synchronized void method() {//定義一個同步方法 if (piao > 0) {//判斷票是否存在 //提高安全問題的出現概率,讓程式睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //票存在,買票,piao-- System.out.println(Thread.currentThread().getName() + "-->正在賣第" + piao + "張票"); piao--; } } }
測試類
public static void main(String[] args) {
//建立Runnable介面的實現類物件
RunnableImpl run = new RunnableImpl();
//建立Thread類物件,構造方法中傳遞Runnable介面的實現類物件
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();//開啟多執行緒
thread2.start();
thread3.start();
/*
出現了執行緒安全問題,賣票出現了重複的票和不存在的票
注意:
執行緒安全問題是不能產生的,我們可以讓一個執行緒訪問共享資料的時候,無論是否失去了cpu的執行權;
讓其他的執行緒只能等待,等待當前程式賣完票,其他執行緒在進行賣票
保證:使用一個執行緒在賣票
*/
}
}
當然同步方法也可以使用靜態方法,原理差不多,大家可以自行摸索
方案三:使用Lock鎖
//實現賣票案例
/*
賣票案例出現了執行緒安全問題
解決執行緒安全問題方法三:使用Lock鎖
java.util.concurrent.locks.Lock介面
Lock實現按提了比使用synchronized方法和 語法可獲得的更廣泛的鎖定操作
Lock介面中的方法:
void lock()獲取鎖
void unlock()釋放鎖
java.util.concurrent.locks.Reentrantlock implements Losk介面
使用步驟:
1.在成員位置建立一個Reentrantlock物件
2.在可能出現安全問題的程式碼前呼叫Lock介面中的方法lock獲取鎖
3.在可能出現安全問題的程式碼後呼叫Lock介面中的方法unlock釋放鎖
*/
public class RunnableImpl implements Runnable {
//定義一個多執行緒共享的票源
private int piao = 100;
//1.在成員位置建立一個Reentrantlock物件
Lock lock = new ReentrantLock();
@Override//設定任務買票
public void run() {
while (true) {//使用死迴圈讓買票操作重複著執行
method();
}
}
public synchronized void method() {//定義一個同步方法
//2.在可能出現安全問題的程式碼前呼叫Lock介面中的方法lock獲取鎖
lock.lock();
if (piao > 0) {//判斷票是否存在
//提高安全問題的出現概率,讓程式睡眠
try {
Thread.sleep(10);
//票存在,買票,piao--
System.out.println(Thread.currentThread().getName() + "-->正在賣第" + piao + "張票");
piao--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//3.在可能出現安全問題的程式碼後呼叫Lock介面中的方法unlock釋放鎖
lock.unlock();//無論是否出現異常,都會把鎖釋放,可以提高程式的效率
}
}
}
}
測試類
/*
1.單執行緒程式是不會出現執行緒安全問題的
2.多執行緒程式,沒有訪問共享資料,不會產生問題
3.多執行緒訪問了共享的資料,會產生執行緒安全問題
*/
//模擬買票案例,建立三個執行緒,同時開啟,對共享的票進行出售
public class Test01 {
public static void main(String[] args) {
//建立Runnable介面的實現類物件
RunnableImpl run = new RunnableImpl();
//建立Thread類物件,構造方法中傳遞Runnable介面的實現類物件
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();//開啟多執行緒
thread2.start();
thread3.start();
/*
出現了執行緒安全問題,賣票出現了重複的票和不存在的票
注意:
執行緒安全問題是不能產生的,我們可以讓一個執行緒訪問共享資料的時候,無論是否失去了cpu的執行權;
讓其他的執行緒只能等待,等待當前程式賣完票,其他執行緒在進行賣票
保證:使用一個執行緒在賣票
*/
}
}
以上就是我總結出來的三種執行緒安全問題的解決方案,希望對大家有所幫助,測試結果沒有發出來,大家可以自行測試一下,學知識重點還是在於理解,學java就是要多敲敲程式碼,也許你不會,敲著敲著你就會了。