java多線程三:線程的同步與死鎖

文章出處 https://www.jianshu.com/p/b913e64db7ee

文章對應視頻出處:https://developer.aliyun.com/course/1012?spm=5176.10731542.0.0.6ef2d290hxQ4g0

在多線程的處理之中,可以利用Runnable描述多個線程操作的資源,而Thread描述每一個線程對象,當然多個線程訪問同一資源時如果處理不當,就會產生數據的錯誤操作。

同步問題的引出

  現在編寫一個簡單的賣票程序,將若干個線程對象實現賣票的處理操作。
範例:實現賣票操作

class MyThread implements Runnable {
    private int ticket = 10;//總票數爲10張
    @Override
    public void run() {
        while (true) {
            if (this.ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
            } else {
                System.out.println("***** 票賣光了 *****");
                break;
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt=new MyThread();
        new Thread(mt,"票販子A").start();
        new Thread(mt,"票販子B").start();
        new Thread(mt,"票販子C").start();
    }
}

  此時的程序將創建3個線程對象,並且這三個線程將進行10張票的出售。此時的程序在進行賣票處理的時候,並沒有任何的問題(假象),下面可以模擬一下賣票中的延遲操作。

class MyThread implements Runnable {
    private int ticket = 10;//總票數爲10張
    @Override
    public void run() {
        while (true) {
            if (this.ticket > 0) {
                try {
                    Thread.sleep(100);//模擬網絡延遲
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
            } else {
                System.out.println("***** 票賣光了 *****");
                break;
            }
        }
    }
}

 這個時候追加了延遲問題就暴露出來了,而實際上這個問題一直都在。

 

線程同步

  經過分析之後已經可以確定同步問題產生的主要原因了,那麼下面就需要進行同步問題的解決,但是解決問題的關鍵是鎖,指的是當某一個線程執行操作的時候,其他線程外面等待;

 如果想程序中實現鎖功能,就可以使用synchronized關鍵字來是吸納,利用synchronized可以定義同步方法和同步代碼塊,在同步代碼塊的操作中的代碼只允許一個線程執行。
1、利用同步代碼塊進行處理:

synchronized (同步對象){
    同步代碼操作;
}

 一般要進行同步對象處理時,可以採用當前對象this進行同步。
  範例:利用同步代碼塊解決數據同步訪問問題

class MyThread implements Runnable {
    private int ticket = 1000;//總票數爲10張
    @Override
    public void run() {
        while (true) {
            synchronized (this) {//每一次只允許一個線程進行訪問
                if (this.ticket > 0) {
                    try {
                        Thread.sleep(100);//模擬網絡延遲
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
                } else {
                    System.out.println("***** 票賣光了 *****");
                    break;
                }
            }
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt, "票販子A").start();
        new Thread(mt, "票販子B").start();
        new Thread(mt, "票販子C").start();
    }
}

  加入同步處理之後,程序的整體性能下降了。同步實際上會造成性能的降低
2、利用同步方法解決:只需要在方法定義上使用synchronized關鍵字即可。

class MyThread implements Runnable {
    private int ticket = 10;//總票數爲10張
    public synchronized boolean sale() {
        if (this.ticket > 0) {
            try {
                Thread.sleep(100);//模擬網絡延遲
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "賣票,ticket = " + this.ticket--);
            return true;
        } else {
            System.out.println("***** 票賣光了 *****");
            return false;
        }
    }
    @Override
    public void run() {
        while (this.sale()) {}
    }
}
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt, "票販子A").start();
        new Thread(mt, "票販子B").start();
        new Thread(mt, "票販子C").start();
    }
}

 在日後學習Java類庫時,系統中許多的類上使用的同步處理採用的都是同步方法。

死鎖

  死鎖是在進行多線程同步的處理之中有可能產生的一種問題,所謂的死鎖指的是若干個線程彼此互相等待的狀態。

public class DeadLock implements Runnable {
    private Producer producer = new Producer();
    private Customer customer = new Customer();
    public DeadLock() {
        new Thread(this).start();
        customer.say(producer);
    }
    public static void main(String[] args) {
        new DeadLock();
    }
    @Override
    public void run() {
        producer.say(customer);
    }
}
class Producer {
    public synchronized void say(Customer customer) {
        System.out.println("店員:先買單後吃飯");
        customer.get();
    }
    public synchronized void get() {
        System.out.println("收到錢,可以給你做飯了");
    }
}
class Customer {
    public synchronized void say(Producer producer) {
        System.out.println("顧客:先吃飯後買單");
        producer.get();
    }
    public synchronized void get() {
        System.out.println("吃飽飯了,可以買單了");
    }
}

 現在死鎖造成的主要原因是因爲彼此都在互相等待着,等待着對方先讓出資源。死鎖實際上是一種開發中出現的不確定的狀態,有時代碼處理不當,則會不定期出現死鎖,這屬於正常開發中的調試問題。
  若干個線程訪問同一資源時一定要進行同步處理,但過多的同步會造成死鎖。

 

 

 

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