Java多執行緒(三)執行緒的同步
阿新 • • 發佈:2021-07-07
JDK中用Thread.State類定義了執行緒的幾種狀態
要想實現多執行緒,必須在主執行緒中建立新的執行緒物件。Java語言使用Thread類及其子類的物件來表示執行緒,在它的一個完整的生命週期中通常要經歷如下的五種狀態: 新建: 當一個Thread類或其子類的物件被宣告並建立時,新生的執行緒物件處於新建狀態。 就緒:處於新建狀態的執行緒被start()後,將進入執行緒佇列等待CPU時間片,此時它已具備了執行的條件,只是沒分配到CPU資源。或者使用yield()方法。 執行:當就緒的執行緒被排程並獲得CPU資源時,便進入執行狀態, run()方法定義了執行緒的操作和功能。當呼叫sleep() join() wait() suspend()方法和等待同步鎖時會由執行到阻塞。 阻塞執行緒的同步:通過同步機制來解決執行緒安全問題
使用synchronized--程式碼同步塊來實現執行緒同步
1 /** 2 * 例子:建立三個視窗賣票,總票數為100張.使用實現Runnable介面的方式 3 * 4 * 1.問題:賣票過程中,出現了重票、錯票 -->出現了執行緒的安全問題5 * 2.問題出現的原因:當某個執行緒操作車票的過程中,尚未操作完成時,其他執行緒參與進來,也操作車票。 6 * 3.如何解決:當一個執行緒a在操作ticket的時候,其他執行緒不能參與進來。直到執行緒a操作完ticket時,其他 7 * 執行緒才可以開始操作ticket。這種情況即使執行緒a出現了阻塞,也不能被改變。 8 * 9 * 10 * 4.在Java中,我們通過同步機制,來解決執行緒的安全問題。 11 * 12 * 方式一:同步程式碼塊 13 * 14 * synchronized(同步監視器){ 15 * //需要被同步的程式碼16 * 17 * } 18 * 說明:1.操作共享資料的程式碼,即為需要被同步的程式碼。 -->不能包含程式碼多了,也不能包含程式碼少了。 19 * 2.共享資料:多個執行緒共同操作的變數。比如:ticket就是共享資料。 20 * 3.同步監視器,俗稱:鎖。任何一個類的物件,都可以充當鎖。 21 * 要求:多個執行緒必須要共用同一把鎖。 22 * 23 * 補充:在實現Runnable介面建立多執行緒的方式中,我們可以考慮使用this充當同步監視器。 24 * 方式二:同步方法。 25 * 如果操作共享資料的程式碼完整的宣告在一個方法中,我們不妨將此方法宣告同步的。 26 * 27 * 28 * 5.同步的方式,解決了執行緒的安全問題。---好處 29 * 操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於是一個單執行緒的過程,效率低。 ---侷限性 30 * 31 */ 32 class Window1 implements Runnable{ 33 34 private int ticket = 100; 35 // Object obj = new Object(); 36 // Dog dog = new Dog(); 37 @Override 38 public void run() { 39 // Object obj = new Object(); 40 while(true){ 41 synchronized (this){//此時的this:唯一的Window1的物件,若使用繼承的方式建立執行緒則不能用this //方式二:synchronized (dog) { 42 43 if (ticket > 0) { 44 45 try { 46 Thread.sleep(100); 47 } catch (InterruptedException e) { 48 e.printStackTrace(); 49 } 50 51 System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); 52 53 54 ticket--; 55 } else { 56 break; 57 } 58 } 59 } 60 } 61 } 62 63 public class WindowTest1 { 64 public static void main(String[] args) { 65 Window1 w = new Window1(); 66 67 Thread t1 = new Thread(w); 68 Thread t2 = new Thread(w); 69 Thread t3 = new Thread(w); 70 71 t1.setName("視窗1"); 72 t2.setName("視窗2"); 73 t3.setName("視窗3"); 74 75 t1.start(); 76 t2.start(); 77 t3.start(); 78 } 79 }
使用同步方法解決Runnable介面的執行緒安全
1 package com.atguigu.java; 2 3 /** 4 * 使用同步方法解決實現Runnable介面的執行緒安全問題 5 * 6 * 7 * 關於同步方法的總結: 8 * 1. 同步方法仍然涉及到同步監視器,只是不需要我們顯式的宣告。 9 * 2. 非靜態的同步方法,同步監視器是:this 10 * 靜態的同步方法,同步監視器是:當前類本身 11 * 12 */ 13 14 15 class Window3 implements Runnable { 16 17 private int ticket = 100; 18 19 @Override 20 public void run() { 21 while (true) { 22 23 show(); 24 } 25 } 26 27 private synchronized void show(){//同步監視器:this 28 //synchronized (this){ 29 30 if (ticket > 0) { 31 32 try { 33 Thread.sleep(100); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 38 System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); 39 40 ticket--; 41 } 42 //} 43 } 44 } 45 46 47 public class WindowTest3 { 48 public static void main(String[] args) { 49 Window3 w = new Window3(); 50 51 Thread t1 = new Thread(w); 52 Thread t2 = new Thread(w); 53 Thread t3 = new Thread(w); 54 55 t1.setName("視窗1"); 56 t2.setName("視窗2"); 57 t3.setName("視窗3"); 58 59 t1.start(); 60 t2.start(); 61 t3.start(); 62 } 63 64 }
使用同步方法解決實現Runnable介面的執行緒安全問題
1 /** 2 * 使用同步方法處理繼承Thread類的方式中的執行緒安全問題 3 */ 4 class Window4 extends Thread { 5 6 7 private static int ticket = 100; 8 9 @Override 10 public void run() { 11 12 while (true) { 13 14 show(); 15 } 16 17 } 18 private static synchronized void show(){//同步監視器:Window4.class 19 //private synchronized void show(){ //同步監視器:t1,t2,t3。此種解決方式是錯誤的 20 if (ticket > 0) { 21 22 try { 23 Thread.sleep(100); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 28 System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); 29 ticket--; 30 } 31 } 32 } 33 34 35 public class WindowTest4 { 36 public static void main(String[] args) { 37 Window4 t1 = new Window4(); 38 Window4 t2 = new Window4(); 39 Window4 t3 = new Window4(); 40 41 42 t1.setName("視窗1"); 43 t2.setName("視窗2"); 44 t3.setName("視窗3"); 45 46 t1.start(); 47 t2.start(); 48 t3.start(); 49 50 } 51 }
同步機制中的鎖
在《Thinking in Java》中,是這麼說的:對於併發工作,你需要某種方式來防止兩個任務訪問相同的資源(其實就是共享資源競爭)。 防止這種衝突的方法就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須鎖定這項資源,使其他任務在其被解鎖之前,就無法訪問它了,而在其被解鎖之時,另一個任務就可以鎖定並使用它了。synchronized中的鎖
任意物件都可以作為同步鎖。所有物件都自動含有單一的鎖(監視器)。
同步方法的鎖:靜態方法(類名.class)、非靜態方法(this)
同步程式碼塊:自己指定,可以指定為this或類名.class
注意:
1. 必須確保使用同一個資源的多個執行緒共用一把鎖,這個非常重要,否則就無法保證共享資源的安全 2. 一個執行緒類中的所有靜態方法共用同一把鎖(類名.class),所有非靜態方法共用同一把鎖(this),同步程式碼塊(指定需謹慎)