下面我們還是用看電影賣票的案例來談一談多線程安全的問題。
方案一:使用同步代碼塊
//實作賣票案例
/*
賣票案例出現線程安全問題
解決方案一:使用同步代碼塊
格式:
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就是要多敲敲代碼,也許你不會,敲着敲着你就會了。