關于多執行緒不安全問題以及解決方法
執行緒不安全
??執行緒不安全就是不提供資料訪問保護,有可能出現多個執行緒先后更改資料造成所得到的資料并不是你所想要的,下面舉例說明:
public class Demo {
/**
* 執行緒不安全
* @param args
*/
public static void main(String[] args) {
//創建一個任務
Runnable run=new Ticket();
//創建三個執行緒,相當于三個售票視窗,它們共同售賣庫存中的10張票
//呼叫start()方法啟動執行緒,開始售票
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count=10;//一共10張票
@Override
public void run() {
while (count>0){
System.out.println("正在售票中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
//Thread.currentThread().getName()獲取當前執行的執行緒的名稱
System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count);
}
}
}
}
運行結果:

??從上面的運行結果我們可以看到余票出現的負數的情況,明明我們在代碼中規定了能進入回圈的條件是count>0,不希望出現票數為負數的情況,但是現在卻出現了,
??這是由于count=1時,Thread-1進入回圈了,但是還沒走到count–的時候,Thread-0和Thread-2也進入了回圈,然后1執行緒執行完count=0,接下來0和2執行緒都相繼進行count–,就出現了負數的情況,像這樣的情況就是執行緒不安全,
執行緒安全
??執行緒安全就是多執行緒訪問時,采用了加鎖機制,當一個執行緒訪問該類的某個資料時,進行保護,其他執行緒不能進行訪問直到該執行緒讀取完,其他執行緒才可使用,不會出現資料不一致或者資料污染,
解決執行緒不安全問題的方法
1.同步代碼塊
因為這個例子的main方法和上面的例子一樣,所以我就不寫main方法了,代碼示例:
static class Ticket implements Runnable{
private int count=10;//一共10張票
Object o=new Object();//創建一個鎖物件
@Override
public void run() {
while (true) {
//當執行緒進入回圈后就會看同步代碼中的鎖物件是否有鎖標記,若有則在這里排隊等待
synchronized (o) {
if (count > 0) {
System.out.println("正在售票中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
}else {
break;//沒有票之后結束回圈
}
}
//當執行緒執行完同步代碼塊中的代碼后就會放開鎖標記,其他的執行緒就可以進入同步代碼塊
}
}
}
運行結果:

現在就不會出現票數為負數的情況了,代碼并沒有出現bug,實在是Thread-0太厲害了,它賣了第一張票之后,直接將鎖打上了標記,別人進不去,它自己又進去賣票了,執行了好多次,Thread-1和Thread-2都搶不到,
2.同步方法
同步方法和同步代碼塊差不多,同步方法就是把需要執行緒排隊的代碼寫到一個方法里,這個方法用synchronized修飾,
static class Ticket implements Runnable{
private int count=10;//一共10張票
//Object o=new Object();//創建一個鎖物件
@Override
public void run() {
while (true) {
sale();
}
}
//定義一個方法,用synchronized修飾
public synchronized void sale(){
if (count > 0) {
System.out.println("正在售票中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
}
}
}
運行結果:

3.顯式鎖Lock
所謂的顯式 就是我們能看得見的,顯式鎖需要我們手動去上鎖和釋放鎖,上面說的同步代碼塊和同步方法都屬于隱式鎖,
static class Ticket implements Runnable{
private int count=10;//一共10張票
//創建一個顯式鎖l,這個顯式鎖是一個公平鎖
Lock l=new ReentrantLock(true);
@Override
public void run() {
while (true) {
l.lock();//鎖住
if (count > 0) {
System.out.println("正在售票中……");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
}else {
break;
}
l.unlock();//釋放鎖
}
}
}
運行結果:

??這次并沒有出現都是Thread-0賣票了,這是因為我在創建顯式鎖的時候傳了一個引數true,這表示創建一個公平的顯式鎖,所謂的公平就是Thread-0執行完之后,輪到后面排隊的Thread-1和Thread-2執行,并不能像上面的同步代碼塊和同步方法那樣,剛執行完又回去打上鎖標記繼續執行,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/258664.html
標籤:其他
上一篇:Tomcat學習筆記01【Web相關概念、Tomcat基本操作】
下一篇:golang確保輸入過濾
