知識點——Java中線程安全問題(共享資源衝突問題)和守護線程

1. 線程安全問題–共享資源能使用問題

例如:
<<湄公河行動>>
100張票

淘票票CGV 美團 貓眼
三個銷售渠道,100張票是一個共享資源!!!
三個銷售渠道,可以認爲是三個銷售線程!!!

問題一:
100張票共享資源問題,選什麼來保存?
局部變量:
在方法內,如果run方法執行,存在,run方法當前執行完畢,銷燬。
每一個線程對象中都有run方法,無法滿足共享問題
成員變量:
每一個線程對象中,都有一個對應的成員變量,非共享資源。
靜態成員變量:
屬於類變量,所有的當前類對象,使用的靜態成員變量都是一個,而且一處修改,處處
受影響。【共享資源】

問題二:
資源衝突問題

線程之間會相互搶佔,而且搶佔頻率很快,有可能會導致一張票賣了三次,也就是資源衝突問題
在這裏插入圖片描述

2. 鎖

所以爲了解決這個問題,我們用鎖來解決

以下鎖的是方法,無論誰調用,怎麼調用都會鎖住

2.1 同步代碼塊

格式:

synchronized (/* 鎖對象 */) {
}

特徵:
1. synchronized 小括號裏面的對象是鎖對象,並且要求如果是多線程的情況下,鎖對象必須是同一個對象。也就是runnable接口實現類對象。
2. synchronized 大括號中的代碼塊就是需要進行同步的代碼,或者說是加鎖的代碼,大括號裏面的內容,有且只允許一個線程進入。
3. 同步代碼塊越短越好,在保證安全的情況下,提高性能

問題:
1. 目前鎖對象感覺很隨意,存在一定的隱患
2. 代碼層級關係很複雜,看着有點麻煩
這個方法比較隨意,一把鎖會鎖了多個線程,有隱患

2.2 同步方法

定義在線程類內

synchronized 作爲關鍵字來修飾方法,修飾的方法就是對應的同步方法

有且只允許一個線程進入,到底是誰來完成的加鎖操作?

  1. 靜態成員方法
    鎖對象,是當前類對應的字節碼文件.class
    就是類名.class

  2. 非靜態成員方法
    鎖對象就是當前類對象 this

選擇同步方法是否使用static修飾問題

  1. 如果非static修飾,要保證執行的線程對象有且只有一個,因爲鎖對象就是當前線程對象

  2. 如果是static修飾,鎖對象具有唯一性,多個線程使用的鎖是同一個鎖。類似於同步代碼塊。

2.3 Lock鎖

Java提供了一個對於線程安全問題,加鎖操作相對於同步代碼塊和同步方法更加廣泛的一種操作方式。

  1. 對象化操作。
    創建Lock構造方法
    Lock lock = new ReentrantLock();
    多態思想,不理解就先記着這麼用。
  2. 方法化操作
    開鎖:
    unlock();
    加鎖:
    lock();

代碼如下:

class SingleThread5 implements Runnable {                                                         
                                                                                                  
	// 共享資源                                                                                       
	private static int ticket = 100;                                                              
	                                                                                              
	// 定義一個成員變量 這裏用到了多態的思想,如果不理解,就先記着這麼用。                                                                                  
	static Lock lock = new ReentrantLock();                                                       
	                                                                                              
	@Override                                                                                     
	public void run() {                                                                           
		while (true) {                                                                            
                                                                                                  
			// lock對象加鎖                                                                           
			lock.lock();                                                                          
			if (ticket > 0) {                                                                     
				System.out.println(Thread.currentThread().getName() + "售出了" + ticket + "張票");     
				ticket -= 1;                                                                      
                                                                                                  
				try {                                                                             
					Thread.sleep(100);                                                            
				} catch (InterruptedException e) {                                                
					// TODO Auto-generated catch block                                            
					e.printStackTrace();                                                          
				}                                                                                 
			} else {                                                                              
				System.out.println(Thread.currentThread().getName() + "售罄!");                     
				break;                                                                            
			}                                                                                     
                                                                                                  
			// 解除鎖                                                                                
			lock.unlock();                                                                        
		}                                                                                         
	}                                                                                             
}                                                                                                 
                                                                                                  
public class Demo5 {                                                                              
	public static void main(String[] args) {                                                      
		                                                                                          
		Thread thread1 = new Thread(new SingleThread5(), "淘票票");                                  
		Thread thread2 = new Thread(new SingleThread5(), "貓眼");                                   
		Thread thread3 = new Thread(new SingleThread5(), "美圖");                                   
		                                                                                          
		thread1.start();                                                                          
		thread2.start();                                                                          
		thread3.start();                                                                          
	}                                                                                             
}                                                                                                 
2.4 三種加鎖方式的總結
  1. 一鎖一線程,一鎖多線程問題。
    使用對應的鎖操作對應的線程,考慮靜態和非靜態問題。
    同步方法和Lock鎖使用。
    靜態是一鎖多目標,非靜態是一鎖一目標

  2. 涉及到同步問題時,要考慮好鎖對象的選擇問題
    同步代碼塊,同步方法,Lock對象。

3. 守護線程

守護線程,也稱之爲後臺線程,如果當前主線程GG思密達,守護線程也就GG思密達。

守護線程一般用於:
1. 自動下載
2. 操作日誌
3. 操作監控

方法是通過線程對象
setDeamon(boolean flag);
true爲守護線程
false缺省屬性,正常線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章