多線程的同步鎖和死鎖(詳細)

    最近發現,編程這東西,一段時間不用,就差很少忘了,感受腦子永遠不夠用,這下利用點時間整理下思路,記錄下來,已被不時之需。
java

         

          


線程:線程是進程中的一個執行單元,負責當前進程中程序的執行,一個進程中至少有一個線程。一個進程中是能夠有多個線程的,這個應用程序也能夠稱之爲多線程程序。簡而言之:一個程序運行後至少有一個進程,一個進程中能夠包含多個線程什麼是多線程呢?即就是一個程序中有多個線程在同時執行。編程

 

單線程程序:即,如有多個任務只能依次執行。當上一個任務執行結束後,下一個任務開始執行。如,去網吧上網,網吧只能讓一我的上網,當這我的下機後,下一我的才能上網。安全

多線程程序:即,如有多個任務能夠同時執行。如,去網吧上網,網吧可以讓多我的同時上網。多線程

 

程序運行原理

 

分時調度併發

 

全部線程輪流使用 CPU 的使用權,平均分配每一個線程佔用 CPU 的時間。dom

 

 搶佔式調度編輯器

 

優先讓優先級高的線程使用 CPU,若是線程的優先級相同,那麼會隨機選擇一個(線程隨機性)Java使用的爲搶佔式調度。ide

 

大部分操做系統都支持多進程併發運行,如今的操做系統幾乎都支持同時運行多個程序。好比:如今咱們上課一邊使用編輯器,一邊使用錄屏軟件,同時還開着畫圖板,dos窗口等軟件。此時,這些程序是在同時運行,感受這些軟件好像在同一時刻運行着測試

 

實際上,CPU(中央處理器)使用搶佔式調度模式在多個線程間進行着高速的切換。對於CPU的一個核而言,某個時刻,只能執行一個線程,而 CPU的在多個線程間切換速度相對咱們的感受要快,看上去就是在同一時刻運行。this

 

其實,多線程程序並不能提升程序的運行速度,但可以提升程序運行效率,讓CPU的使用率更高

 

 線程安全

 

若是有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的

 

/**
 * 
 * @author LYJ
 *    實現Runnable的代碼
 *    
 */
public class Ticket implements Runnable {
    //設置總票數爲100,這裏的ticket是成員變量,
    //因爲在測試類中new了一次,因此值存在一個,被三個售票窗口共享
    int ticket=100;
    public void run() {
        //模擬售票
        while(true) {
            //若是票數大於0,繼續售票
            if(ticket>0) {
                //爲了讓線程安全問題效果明顯些,加入線程定時休眠Thread.sleep()
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //Thread.currentThread()是線程獲取當前線程對象的方法    getName()獲取調用者的線程名
                System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--);
            }
        }

    }

/**
 * 
 * 開啓多線程的代碼
 *
 */
public class ThreadDemo01 {
    public static void main(String[] args) {
        //建立Ticket的Runnable對象
        Ticket ticket = new Ticket();
        //建立線程3個對象模擬三個售票窗口,並把Runnable對象加入Thread和給Thread命名
        new Thread(ticket,"窗口1").start();;
        new Thread(ticket,"窗口2").start();;
        new Thread(ticket,"窗口3").start();;

    }
*****************************************************************
輸出結果:
窗口3正在售票:3
窗口2正在售票:2
窗口1正在售票:1
窗口3正在售票:0
窗口2正在售票:-1
            結果中出現了負數和0,這就是線程安全問題,要怎麼解決呢?
            加同步鎖 synchronized(Object o){....} o能夠是任意對象
******************************************************************************
加入同步鎖後的代碼
public class Ticket implements Runnable {
    //設置總票數爲100,這裏的ticket是成員變量,
    //因爲在測試類中new了一次,因此值存在一個,被三個售票窗口共享
    int ticket=100;
    public void run() {
        //模擬售票
        while(true) {
            //若是票數大於0,繼續售票
            
            //加入同步鎖
            synchronized(this) {
            if(ticket>0) {
                //爲了讓線程安全問題效果明顯些,加入線程定時休眠Thread.sleep()
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //Thread.currentThread()是線程獲取當前線程對象的方法    getName()獲取調用者的線程名
                System.out.println(Thread.currentThread().getName()+"正在售票:"+ticket--);
                }
            }
        }

    }
**********************************************
運行幾回,發現運行結果中沒有出現負數和0

 

同步方法:在方法聲明上加上synchronized

public synchronized void method(){

    可能會產生線程安全問題的代碼

}  

同步方法中的鎖對象是 this(即調用者對象

靜態同步方法: 在方法聲明上加上static synchronized

public static synchronized void method(){

可能會產生線程安全問題的代碼

}

靜態同步方法中的鎖對象是 類名.class(由於在加載類文件的時候,靜態同步方法因爲是靜態的也被加載進內存了,類名.class的加載優先級高於靜態方法

同步代碼塊:在須要同步的代碼外面包上一個synchronized

(Object o){

    可能會產生線程安全問題的代碼

} 

同步代碼塊中的所對象能夠是任意對象

 

死鎖

 

同步鎖使用的弊端:當線程任務中出現了多個同步(多個鎖)時,若是同步中嵌套了其餘的同步。這時容易引起一種現象:程序出現無限等待,這種現象咱們稱爲死鎖。這種狀況能避免就避免掉。

 

synchronzied(A){

 

  synchronized(B){

 

         

 

  }

 

}

/**
 * 
 *建立鎖對象
 *
 */
public class Lock {
    //這裏用private封裝,爲了避免讓外面隨便造鎖,限制只能有A,B鎖個一把,這樣容易出現死鎖
    //即A同窗和B同窗想相互串門,但是沒人只有一把本身房間的鑰匙,並且各自都不肯意先給,因而死鎖
    private Lock() {};
    public static final Object lockA =new Object();
    public static final Object lockB = new Object();
    //這裏使用static 爲了讓外界能夠經過類名調用成員變量lockA和lockB
    //由於外面沒法建立Lock對象,爲了讓外面在不創對象的狀況下調用,加了static,經過類名加變量名訪問
}

/**
 * 線程任務類
 * 
 */
import java.util.Random;

public class ThreadTask implements Runnable {
    int x = new Random().nextInt(1);//用隨機數隨機獲取0、1,來模擬CPU隨機分配執行權的行爲
    @Override
    public void run() {
        while(true) {
            if(x%2==0) {
                //狀況一
//                先執行A再執行B:即A同窗先拿了A門的鑰匙去開A門,而後打算開B門
                synchronized(Lock.lockA) {
                    System.out.println("A同窗...開A門");
                    synchronized(Lock.lockB) {
                        System.out.println("A同窗...開B門");
                    }
                }
            }else {
                //狀況二
//                先執行B執行A:B同窗先拿了B門的鑰匙,去開B門,而後打算開A門
                synchronized(Lock.lockB) {
                    System.out.println("B同窗...開B門");
                    synchronized(Lock.lockA) {
                        System.out.println("B同窗...開A門");
                    }
                }
            }
            x++;
        }

    }

/**
 * 
 * 線程測試類
 *
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //建立Runnable的實現類對象
        ThreadTask tt = new ThreadTask();
        //把Runnable實現類對象加入線程中,建立2個線程
        Thread t1 = new Thread(tt);
        Thread t2 = new Thread(tt);
        t1.start();
        t2.start();
        
    }
*********************************************************
輸出結果:A同窗...開A門
     A同窗...開B門
     B同窗...開B門
     B同窗...開A門
     A同窗...開A門
     B同窗...開B門
結論:A同窗或者B同窗,一我的前後拿走兩把鑰匙時,線程是正常運行的,一旦A拿了A鎖進去A門的時候,CPU忽然讓B開始執行,讓B拿了B鎖進入B門,結果A須要B鎖,B也須要A鎖,二者又不能後退
因而死鎖現象發生了。

 等待喚醒機制

 

線程之間的通訊:多個線程在處理同一個資源,可是處理的動做(線程的任務)卻不相同。經過必定的手段使各個線程能有效的利用資源。而這種手段即等待喚醒機制

相關文章
相關標籤/搜索