首頁 >Java >java教程 >Java線程安全與同步實例分析

Java線程安全與同步實例分析

WBOY
WBOY轉載
2023-04-29 20:58:05874瀏覽

執行緒安全問題

多個執行緒可能會共用(存取)同一個資源

例如存取同一個對象,同一個變量,同一個檔案

當當多個執行緒存取同一塊資源時,很容易引發資料錯亂和資料安全問題,稱為執行緒安全問題

什麼情況下會出現執行緒安全問題

多個執行緒共享同一個資源

且至少有一個執行緒正在執行寫的操作

#實例:

#存錢買問題

分別有存錢和取錢2個執行緒

   存錢                     取款項
   線程1         1000 1000---- -》2000
                500 《-----1000-500

正確:結束後餘額應該是1500,而不是500

正確:結束後餘額應該是1500,而不是500

問題正確:結束後餘額應該是1500,而不是500

問題正確:結束後餘額應該是1500,而不是500


問題正確:結束後餘額應該是1500,而不是500

問題正確:結束後餘額應該是1500,而不是500

正確:結束後餘額應該是1500,而不是500

Java線程安全與同步實例分析#有賣票2個主題

   賣票                     賣票
    1      10 票數00
1000-1-----》999

                999 《-----1000-1

正確:結束後餘額應該是998,而不是999

買票問題錯誤(未執行緒同步)實例:

public class love implements Runnable{
    private int piao=3000;//有3000张票
    public boolean sale() {//ture代表还有票;false代表没有票了
        if(piao<1) return false;
         piao--;//卖1张票
         
         //细化piao--;
         //寄存器=piao;
         //寄存器=寄存器-1;
         //piao=寄存器;
         
         String sk =Thread.currentThread().getName();//获取当前线程(买票窗口)的名字
         System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
         return piao>1;
    }
    public void run() {
         while(sale());//循环执行;直至卖完票返回false
    }
}
 
public class Main {
    public static void main(String[] a) {
        love tjlove =new love();
        for(int i=1;i<=4;i++) {//循环4次;产生4个线程(窗口)卖票
            Thread tj = new Thread(tjlove());
            tj.setName(""+i);
            tj.start();
        }
    }
}

部分輸出結果:



#執行緒安全問題

#分析問題

線程A和B對類別中1個變數值為17進行1操作

最終結果為2個18

解決方案

加鎖:

過程:首先線程A先訪問到這個17,讀上來後進行加鎖並進去1的操作改為18

並且17在加鎖期間其它線程都不能訪問

改完之後再進行寫入,然後再解鎖17

然後再由線程B去訪問它,再進行加鎖,重複上面操作變成19再解鎖

這樣做能保證在同一時間只有1個線程去訪問它,這樣就保證了安全;之前錯誤是由於這些線程一起去訪問了它

線程同步Java線程安全與同步實例分析

剛剛所說的加鎖操作便是線程同步技術

#可以使用執行緒同步技術來解決執行緒安全性問題

執行緒同步在Java裡有2種做法:

#1.同步語句

2.同步方法

同步語句

public class love implements Runnable{
	private int piao=3000;//本人cpu单核性能过强,数据量大些才能看到是4个线程在卖票
	public boolean sale() {
		synchronized(this) {//1个线程获取这个对象的锁,并加锁;    synchronized作用于整个语句
		//this指向当前对象
		//不能用new Object();这样会产生新的对象,产生新的锁
		//把this换成"123",效果基本一样;因为其存在常量值里,每次访问的对象一样
			if(piao<1) return false;
			piao--;
			String sk =Thread.currentThread().getName();
			System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
			return piao>0;
			}
	}
	public void run() {
		 while(sale());
	}
}

部分輸出結果:

synchronize(obj)的原理

#1.每個物件都有一個與它相關的內部鎖(intrinsic lock)或叫監視器鎖(monitor lock)

2.第一個執行到同步語句的執行緒可以獲得obj 的內部鎖,執行完同步語句中的程式碼後釋放此鎖定

3.只要一個執行緒持有了內部鎖定,那麼其它執行緒在同一時刻將無法再取得此鎖定

✓ 當它們試圖取得此鎖定時,將會進入BLOCKED狀態

4.多個執行緒存取同一個synchronized(obj)語句時

obj必須是同一個對象,才能起到同步的作用

同步方法

public class love implements Runnable{
    private int piao=3000;
    public synchronized boolean sale() { //synchronized作用于整个方法
            if(piao<1) return false;
            piao--;
            String sk =Thread.currentThread().getName();
            System.out.println(sk+"卖了1张票,还剩下"+piao+"张");
            return piao>0;
    }
    public void run() {
         while(sale());
    }
}

synchronized不能修飾建構方法

同步方法的本質

###實例方法:synchronized (this)######靜態方法:synchronized (Class物件)## ####同步語句比同步方法更靈活一點######同步語句可以精確控制需要加鎖的程式碼範圍,減少處於BLOCKED狀態的執行緒,充分利用勞動力######使用了執行緒同步技術後######雖然解決了線程安全問題,但是降低了程式的執行效率######因為加了鎖就會有處於等待的線程,多了加鎖解鎖的操作### ###所以在真正有必要的時候,才使用執行緒同步技術###

以上是Java線程安全與同步實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除