Java 高併發編程 -死鎖-生產者消費者模式

線程同步:就是在執行代碼的時候,同一個資源,被多個線程訪問,此時協調這多個線程得訪問就是線程同步。

解決比較簡單的一種方法是,實用synchronized 同步鎖,讓下一個線程等待上一個線程執行結束。然後執行,在上synchronized鎖得時候,需要注意,這把鎖的粒度。

在上synchronized鎖得時候經常會碰見的一個問題是,『死鎖』

前提:倆個或以上的線程
線程A,整個操作過程中鎖住了a(先)對象和b(後)對象。
線程B,整個操作過程中鎖住了b(先)對象和a(後)對象。

線程A 將a對象執行完了,執行到b對象的時候,發現b對象已經被線程B鎖定,那麼線程A就在等待中,a的鎖是不會釋放。
線程B執行完b對象的操作的時候,發現a對象已經被A線程給鎖定,於是也進入等待,此時b得鎖也是不會釋放。

於是線程A和線程B都等待對方釋放鎖,於是兩個線程就都執行不下去了
public class Account implements Runnable{
    public int flag = 1;
    static Object o1 = new Object();
    static Object o2 = new Object();
    @Override
    public void run() {
        System.out.println("flag= " + flag);
        if (flag == 1){
            synchronized (o1){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println("1");
                }
            }
        }
        if (flag==0){
            synchronized (o2){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (o1){
                    System.out.println("0");
                }
            }
        }
    }
    public static void main(String[] args){
        Account t1 = new Account();
        Account t2 = new Account();

        t1.flag=1;
        t2.flag=0;

        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();
        thread2.start();
    }
}

程序會一直在這裏執行,

解決死鎖得方法:

  • 最好只鎖住一個,不要鎖住多個,也就是將鎖得粒度加粗一些
  • 鎖定當前整個對象就可以了,爲什麼要在這個對象下面鎖定兩個小對象呢?
  • 鎖的粒度加粗些,當然還有很多其它的辦法

生產者-消費者模式

package Basic;

public class ProducerConsumer{

    public static void main(String[] args){
        SyncStack syncStack = new SyncStack();
        Producer producer = new Producer(syncStack);
        Consumer consumer = new Consumer(syncStack);

        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

/*
    饅頭類
    id: 每一個饅頭得id
 */
class WoTou{
    int id;

    public WoTou(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "WoTou{" + "id=" + id + '}';
    }
}

class SyncStack{
    int index = 0;
    WoTou[] arrWT = new WoTou[6];
    /*
        裝饅頭
     */
    public synchronized void push(WoTou woTou){
        while (index == arrWT.length){
            /*
                這裏主要的是 wait 這個等待方法,執行這個方法的前提是 synchronized 將這個對象鎖住
                鎖住之後才能去 wait 等待,等待過程中會釋放鎖

                當等待之後,這個對象的鎖就不在歸我管理了,只有當我醒來的時候,採取找這把鎖

                當一個線程來給我塞饅頭得時候,如果這個時候,饅頭已經塞滿了,不能再往裏面塞了
                那麼就要去阻塞住,

                等待消費者將饅頭買完,才能繼續生產(塞饅頭)
             */

            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        /*
            wait 和 notify 得方法是一致的,
            notify 方法叫醒一個正在wait在我這個對象上的方法,讓他繼續執行
            notifyAll 方法叫醒所有正在wait在我這個對象上的方法,讓他繼續執行
         */
        this.notify();
        arrWT[index] = woTou;
        index ++;
    }
    /*
        吃饅頭
     */
    public synchronized WoTou pop(){
        while (index==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        this.notify();
        index --;
        return arrWT[index];
    }
}

/*
    生產者一次生產 20 個饅頭
 */
class Producer implements Runnable{
    SyncStack ss = null;

    public Producer(SyncStack ss) {
        this.ss = ss;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            WoTou woTou = new WoTou(i);
            ss.push(woTou);
            System.out.println("生產了:" + woTou);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/*
    消費者一次吃 20 個饅頭
 */
class Consumer implements Runnable{
    SyncStack ss = null;

    public Consumer(SyncStack ss) {
        this.ss = ss;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            WoTou pop = ss.pop();
            System.out.println("消費了:" + pop);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

原文鏈接

 

 

 

 

 

 

 

 

 

 

 

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