【Android 系統開發】_「進程線程」篇 -- 「同步」 和 「死鎖」

Thread - 同步

問題引出

咱們如今來經過Runnable接口實現多線程,產生3個線程對象,模擬賣票的場景!java

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (ticket > 0) {
                try {
                    Thread.sleep(300);     // 加入延遲
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("賣票:ticket = " + ticket--);
           }
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱們執行下這段代碼,結果以下:多線程

賣票:ticket = 5
賣票:ticket = 4
賣票:ticket = 5
賣票:ticket = 3
賣票:ticket = 2
賣票:ticket = 2
賣票:ticket = 1
賣票:ticket = 0
賣票:ticket = -1    // 票數居然還能負數?

爲何會出現「負數」的狀況:在上面的操做中,咱們能夠發現,由於加入了「延遲操做」一個線程頗有可能在還沒對票數進行減操做以前,其餘線程就已經將票數減小了,這樣就會出現票數爲負的狀況。併發

有沒有方法解決?確定是有的!想解決這樣的問題,就必須使用同步!所謂同步,就是指多個操做在同一個時間段內只能有一個線程進行,其餘線程要等待此線程完成以後才能夠繼續執行。this

解決問題

解決資源共享的同步操做,有兩種方法:同步代碼塊同步方法線程

同步代碼塊

所謂代碼塊就是指使用「{}」括起來的一段代碼,若是在代碼塊上加上synchronized關鍵字,則此代碼塊就成爲同步代碼塊。code

【同步代碼塊 - 格式】對象

synchronized(同步對象) {
    須要同步的代碼 ;
}

咱們對代碼進行修改:接口

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (this) {              // 加入同步操做
                if (ticket > 0) {
                    try {
                        Thread.sleep(300);     // 加入延遲
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("賣票:ticket = " + ticket--);
               }
            }
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱們從新執行下這段代碼,結果以下:進程

賣票:ticket = 5
賣票:ticket = 4
賣票:ticket = 3
賣票:ticket = 2
賣票:ticket = 1

同步方法

除了能夠將須要的代碼設置成同步代碼塊外,也可使用synchronized關鍵字將一個方法聲明成同步方法。資源

【同步方法 - 格式】

synchronized 方法返回值 方法名稱(參數列表) {
}

咱們採用同步方法對代碼進行修改:

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            this.sale();                // 調用同步方法
        }
    }
    public synchronized void sale() {   // 聲明同步方法
        if (ticket > 0) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("賣票:ticket = " + ticket--);
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱們從新執行下這段代碼,結果以下:

賣票:ticket = 5
賣票:ticket = 4
賣票:ticket = 3
賣票:ticket = 2
賣票:ticket = 1

從以上程序的運行結果能夠發現,此代碼完成了與以前同步代碼塊一樣的功能。

總結

多個線程共享同一資源時須要進行同步,以保證資源操做的完整性。

Thread - 死鎖

經過上面的例子,咱們發現,同步仍是頗有好處的,它能夠保證資源共享操做的正確性,可是過多的同步也會產生問題,這就是咱們接下來要討論「死鎖」問題!

什麼是死鎖?

多線程以及多進程改善了系統資源的利用率並提升了系統 的處理能力。然而,併發執行也帶來了新的問題 -- 死鎖

在編寫多線程的時候,必需要注意資源的使用問題,若是兩個或多個線程分別擁有不一樣的資源,而同時又須要對方釋放資源才能繼續運行時,就會發生死鎖。

簡單來講:死鎖就是當一個或多個進程都在等待系統資源,而資源自己又被佔用時,所產生的一種狀態。

形成死鎖的緣由

多個線程競爭共享資源,因爲資源被佔用,資源不足或進程推動順序不當等緣由形成線程處於永久阻塞狀態,從而引起死鎖。

形成死鎖的四個條件

一、互斥條件:進程對於所分配到的資源具備排它性,即一個資源只能被一個進程佔用,直到被該進程釋放
二、請求和保持條件:一個進程因請求被佔用資源而發生阻塞時,對已得到的資源保持不放。
三、不剝奪條件:任何一個資源在沒被該進程釋放以前,任何其餘進程都沒法對他剝奪佔用
四、循環等待條件:當發生死鎖時,所等待的進程一定會造成一個環路(相似於死循環),形成永久阻塞。

問題引出

如今張三想要李四的畫,李四想要張三的書,因而產生了如下對話:

張三對李四說:「把你的畫給我,我就給你書」
李四對張三說:「把你的書給我,我就給你畫」

此時,張山在等着李四的答覆,李四也在等着張三的答覆,那麼這樣下去的結果就是,兩我的都在等待,可是都沒有結果,這就是「死鎖」!

從線程角度來講,所謂死鎖就是指兩個線程都在等待彼此先完成,形成了程序的停滯,通常程序的死鎖都是在程序運行時出現,好比咱們經過一個代碼範例來看看發生死鎖的場景。

class Zhangsan {
    public void say() {
        System.out.println("Zhangsan say: give me your painting, i will give you my book!");
    }
    
    public void get() {
        System.out.println("Zhangsan got Lisi's painting!");
    }
}

class Lisi {
    public void say() {
        System.out.println("Lisi say: give me your book, i will give you my painting!");
    }
    
    public void get() {
        System.out.println("Lisi got Zhangsan's book!");
    }
}

public class ThreadDeadLock implements Runnable {
    private static Zhangsan zs = new Zhangsan();         // 實例化static型對象,數據共享
    
    private static Lisi ls = new Lisi();                 // 實例化static型對象,數據共享
    
    private boolean flag = false;                        // 聲明標記,用於判斷哪一個對象先執行
    
    public void run() {
        if (flag) {                                      // 判斷標誌位,flag爲true,Zhangsan先執行
            synchronized (zs) {                          // 同步第一個對象
                zs.say();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (ls) {                      // 同步第二個對象
                    zs.get();
                }
            }
        } else {                                         // Lisi先執行
            synchronized (ls) {                          // 同步第二個對象
                ls.say();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (zs) {                      // 同步第一個對象
                    ls.get();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadDeadLock t1 = new ThreadDeadLock();
        ThreadDeadLock t2 = new ThreadDeadLock();
        t1.flag = true;
        t2.flag = false;
        Thread thA = new Thread(t1);
        Thread thB = new Thread(t2);
        thA.start();
        thB.start();
    }
}

咱們執行下這段代碼,結果以下:

Zhangsan say: give me your painting, i will give you my book!
Lisi say: give me your book, i will give you my painting!

從程序的運行結果中能夠看出,兩個線程都在彼此等待着對方的執行完成,這樣,程序就沒法向下繼續執行,從而形成了死鎖的現象。

解決問題

要預防和避免死鎖的發生,只需將上面所講到的4個條件破壞掉其中之一便可。

如上面的代碼當中,有四個同步代碼塊,只須要將其中一個同步代碼塊去掉,便可解決死鎖問題,通常而言破壞「循環等待」這個條件是解決死鎖最有效的方法。

相關文章
相關標籤/搜索