java複習第6天---多線程----6.3--多線程同步

java複習第6天---多線程----6.3--多線程同步


目錄




內容

1、線程同步

1.1、多線程安全問題的產生

  • 示例1.1-1:電影院售票問題

  現在電影院正在出售流浪地球電影票,100張。

  • 只有1個售票窗口(單線程):此時不會產生安全問題
  • 有多個售票窗口(多線程):當每個窗口售票都不一樣時,比如100張票分開,每個窗口分別銷售一疊票。此時,也不會出現安全問題
  • 多個售票窗口(多線程):每個售票窗口都銷售者100張票,不分開。這時候,可能產生安全問題:
  • 圖示1.1-1:[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-A6mdhWOB-1590502296256)(./images/sealTicket.bmp)]

1.2、多線程安全問題產生代碼實現

package thread.create;

public class Ticket implements Runnable{

	private int ticket = 10;

	@Override
	public void run() {
		while(true) {
			if(this.ticket > 0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票");
				ticket--;
			}
		}
	}
}

package thread.create;

public class TestTicket1 {
	public static void main(String[] args) {
		Ticket t = new Ticket();
		Thread t1 = new Thread(t, "一號窗口");
		Thread t2 = new Thread(t, "二號窗口");
		Thread t3 = new Thread(t, "三號窗口");

		t1.start();
		t2.start();
		t3.start();
	}

}
測試結果:
一號窗口正在售賣10號票
三號窗口正在售賣9號票
二號窗口正在售賣9號票
二號窗口正在售賣7號票
一號窗口正在售賣7號票
三號窗口正在售賣7號票
一號窗口正在售賣4號票
三號窗口正在售賣4號票
二號窗口正在售賣4號票
三號窗口正在售賣1號票
二號窗口正在售賣0號票
一號窗口正在售賣-1號票

1.3、多線程安全問題分析

  某一時刻在打印語句輸出之前,3個線程ticket值可能相同,所有會打印相同的票,當某一個線程線程執行tickent = 0時,另外要給線程剛好執行tickent–那麼可能打印-號票。歸根結底,還是多線程共享同一資源,並且對共享資源進行操作引起的安全問題,怎麼解決呢?
  爲解決多線程安全問題,java引入同步機制。同步機制有三種方式:

  • 同步代碼塊
  • 同步方法
  • 鎖機制

下面分別介紹使用方法。

1.4、同步代碼

  synchronized 關鍵字可以作用於方法中的某一塊區域,表示只對這個區域內的資源進行互斥訪問。

  • 格式

      synchronized(同步鎖) {
      	需要同步的代碼
      }
    
  • 同步鎖:對象的同步鎖只是一個概念,可以想象在對象上標記了一個鎖

    1. 所對象可以是任意類型
    2. 多個線程共享同一資源,需要使用相同的鎖
    3. 在任何時刻,最多有一個線程獲取同步鎖,進入代碼塊執行,其他線程對象只能在外等着(BOLOCKED)
  • 同步代碼塊解決多線程安全問題:

      package thread.create;
    
      public class SynTicket implements Runnable{
      private int ticket = 10;
    
      	@Override
      	public void run() {
      		while(true) {
      			synchronized(this) {
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票");
      					ticket--;
      				}
      			}
      		}
      	}
      }
      
      package thread.create;
    
      public class TestSynTicket1 {
      	public static void main(String[] args) {
      		SynTicket t = new SynTicket();
      		Thread t1 = new Thread(t, "一號窗口");
      		Thread t2 = new Thread(t, "二號窗口");
      		Thread t3 = new Thread(t, "三號窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      測試結果:
      一號窗口正在售賣10號票
      一號窗口正在售賣9號票
      一號窗口正在售賣8號票
      二號窗口正在售賣7號票
      三號窗口正在售賣6號票
      二號窗口正在售賣5號票
      一號窗口正在售賣4號票
      二號窗口正在售賣3號票
      三號窗口正在售賣2號票
      三號窗口正在售賣1號票
    

1.5、同步方法

  • 同步方法:用synchronized 修飾的方法爲同步方法,能保證一個線程在訪問該方法的時候,其他線程在方法外等待

  • 格式

      修飾符 synchronized 返回值類型 方法名(參數列表) {需要同步的代碼塊}
    
  • 所對象:

    • 非static方法,所對象爲this
    • static方法,鎖對象爲方法所在類的字節碼(.class)
  • 用同步方法解決賣票完全問題:

      package thread.create;
    
      public class SynMethodTicket implements Runnable{
      private int ticket = 10;
    
      	@Override
      	public synchronized void run() {
      		while(true) {
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票");
      					ticket--;
      				}
      		}
      	}
    
      }
      
      package thread.create;
    
      public class TestSynMethodTicket {
      	public static void main(String[] args) {
      		SynMethodTicket t = new SynMethodTicket();
      		Thread t1 = new Thread(t, "一號窗口");
      		Thread t2 = new Thread(t, "二號窗口");
      		Thread t3 = new Thread(t, "三號窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      測試結果:
      一號窗口正在售賣10號票
      一號窗口正在售賣9號票
      一號窗口正在售賣8號票
      一號窗口正在售賣7號票
      一號窗口正在售賣6號票
      一號窗口正在售賣5號票
      一號窗口正在售賣4號票
      一號窗口正在售賣3號票
      一號窗口正在售賣2號票
      一號窗口正在售賣1號票
    
  • 總結:

    • 鎖作用代碼塊越少,效率相對越高
    • 能用同步代碼塊儘量不要用同步方法

1.6、lock鎖

  java.util.concurrent.locks.Lock 鎖提供了比同步代碼塊和同步方法更廣泛的鎖定操作,具有同步代碼塊和同步方法全部功能,更加體現面向對象。
  Lock鎖也稱同步鎖,加鎖和釋放鎖方法化 了,如下

  • public void lock:加鎖

  • public void unlock:釋放鎖

  • 使用步驟:

    1. 成員位置創建ReentrantLock對象
    2. 在可能出現安全問題的代碼前 使用lock.lock()方法加鎖
    3. 在可能出現安全問題的代碼後使用lock.unlock()方法釋放鎖
  • lock鎖解決買票安全問題:

      package thread.create;
    
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
    
      public class TicketLock implements Runnable{
      private int ticket = 10;
      Lock lock = new ReentrantLock();	
      	@Override
      	public void run() {
      		while(true) {
      				lock.lock();
      				if(this.ticket > 0) {
      					try {
      						Thread.sleep(100);
      					} catch (InterruptedException e) {
      						// TODO Auto-generated catch block
      						e.printStackTrace();
      					}
      					System.out.println(Thread.currentThread().getName() + "正在售賣" + this.ticket + "號票");
      					ticket--;
      				}
      				lock.unlock();
      		}
      	}
    
      }
      
      package thread.create;
    
      public class TestTicketLock {
      	public static void main(String[] args) {
      		TicketLock t = new TicketLock();
      		Thread t1 = new Thread(t, "一號窗口");
      		Thread t2 = new Thread(t, "二號窗口");
      		Thread t3 = new Thread(t, "三號窗口");
    
      		t1.start();
      		t2.start();
      		t3.start();
      	}
      }
      測試結果:
      一號窗口正在售賣10號票
      一號窗口正在售賣9號票
      一號窗口正在售賣8號票
      一號窗口正在售賣7號票
      一號窗口正在售賣6號票
      三號窗口正在售賣5號票
      三號窗口正在售賣4號票
      三號窗口正在售賣3號票
      二號窗口正在售賣2號票
      二號窗口正在售賣1號票
    

1.7、靜態同步方法

  靜態方法的鎖對象是所在類的class屬性,即所在類的字節碼類。

後記

本項目爲參考某馬視頻開發,相關視頻及配套資料可自行度娘或者聯繫本人。上面爲自己編寫的開發文檔,持續更新。歡迎交流,本人QQ:806797785

前端項目源代碼地址:https://gitee.com/gaogzhen/vue-leyou
    後端JAVA源代碼地址:https://gitee.com/gaogzhen/JAVA

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