線程協作的三種方式

線程之間需要進行通信,通信有數據共享和線程協作兩種方式,這篇主要說線程協作的內容。

一:數據共享

1:文件共享;2:網絡共享;3:變量共享。

二:線程協作

        先來個場景:落魄程序員擺攤賣起了炒粉,起先有人去買炒粉,發現炒粉賣完了,只能失落的回家了;後來爲了不讓客戶白來一趟,落魄程序員想到了一個辦法,線上預定。要是沒有炒粉了,客戶就不要白跑了,要是炒粉做好了,就通知客戶。

2.1 被棄用的suspend和resume

suspend會讓當前線程掛起,resume會喚醒當前線程。那麼,舉個栗子先:

    /** 炒粉對象 */
    public static Object obj;

    public static void main(String[] args) throws InterruptedException {
        Demo_Suspend suspend = new Demo_Suspend();
        suspend.test1();
    }

    //不會死鎖
    public void test1() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                    //沒有買到炒粉,就掛起等待
                    Thread.currentThread().suspend();
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        //炒粉做好了,就喚醒客戶
        thread.resume();
        System.out.println("落魄程序員做好了炒粉,通知客戶。");
    }

上述代碼運行結果如下:

第二個栗子:

   //死鎖,加了synchronized關鍵字
   public void test2() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    synchronized (Demo_Suspend.class) {
                        System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                        //沒有買到炒粉,就掛起等待
                        Thread.currentThread().suspend();
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        synchronized (Demo_Suspend.class) {
            //炒粉做好了,就喚醒客戶
            thread.resume();
            System.out.println("落魄程序員做好了炒粉,通知客戶。");
        }
    }

運行結果:

       發現線程一直掛起了,區別就是加了synchronized鎖,客戶買不到就把自己關了起來,想要通知到客戶,就要拿到鑰匙,可是客戶把鑰匙也拿起來了,所以就通知不到,造成了死鎖。

      注意:那是因爲在synchronized中沒有釋放鎖這個語義的,所以鎖不能夠進行釋放,別人也就獲取不到,然後就是死鎖了

第三個栗子:

    //先喚醒,後掛起,會造成死鎖
    public void test3() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    try {
                        Thread.sleep(5000);
                        System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                        //沒有買到炒粉,就掛起等待
                        Thread.currentThread().suspend();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        //炒粉做好了,就喚醒客戶
        thread.resume();
        System.out.println("落魄程序員做好了炒粉,通知客戶。");
    }

上述例子呢,落魄程序員2點就把炒粉做好了,去通知客戶,可是客戶5點了還在睡覺,運行結果如下:

同樣的,會造成死鎖。

2.2 wait和notify,notifyAll

      wait會讓當前線程掛起,而且當線程調用wait之後,會自動釋放鎖,notify,notifyAll會喚醒線程,wait和notify,notifyAll只能用在synchronized關鍵字中,而且必須是同一個對象鎖,否則會報java.lang.IllegalMonitorStateException異常。

又來栗子了:

    public static Object obj;

    public static void main(String[] args) throws InterruptedException {
        Demo_Wait wait = new Demo_Wait();
        wait.test1();

    }

    //正常,不會死鎖
    public void test1() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    synchronized (Demo_Wait.class) {
                        try {
                            System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                            Thread.currentThread().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        synchronized (Demo_Wait.class) {
            Thread.currentThread().notifyAll();
            System.out.println("落魄程序員做好了炒粉,通知客戶。");
        }
    }
運行結果如下:

下一個栗子:

    //先喚醒後掛起會死鎖
    public void test2() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    synchronized (Demo_Wait.class) {
                        try {
                            Thread.sleep(6000);
                            System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                            Thread.currentThread().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        synchronized (Demo_Wait.class) {
            Thread.currentThread().notifyAll();
            System.out.println("落魄程序員做好了炒粉,通知客戶。");
        }
    }

 同樣的,落魄程序員2點就把炒粉做好了,去通知客戶,可是客戶6點了還在睡覺,造成了死鎖。運行結果如下:

2.3 park和unpark

park會讓線程掛起,unpark喚醒線程。話不多說,栗子來啦:

    public static Object obj;

    public static void main(String[] args) throws InterruptedException {
        Demo_park park = new Demo_park();
        park.test1();
    }

    //不會死鎖
    public void test1() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                    //沒有買到炒粉,就掛起等待
                    LockSupport.park();
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        //炒粉做好了,就喚醒客戶
        LockSupport.unpark(thread);
        System.out.println("落魄程序員做好了炒粉,通知客戶。");
    }

運行結果如下:

第二個栗子:

   //先喚醒後掛起不會死鎖
    public void test2() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    try {
                        Thread.sleep(6000);
                        System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                        //沒有買到炒粉,就掛起等待
                        LockSupport.park();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        LockSupport.unpark(thread);
        System.out.println("落魄程序員做好了炒粉,通知客戶。");
    }

運行結果:

       發現,先喚醒後掛起並不會死鎖,原因是park,unpark是許可的意思,也就是說你只要有了許可證,就可以通過,若unpark多次,只當作一次,後續過來的park會繼續等待。

第三個栗子:

    //加了sync會死鎖
    public void test3() throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (obj == null) {
                    synchronized (Demo_park.class) {
                        System.out.println("客戶來預定炒粉,炒粉賣完了,不開心。。。");
                        LockSupport.park();
                    }
                }
                System.out.println("客戶買到了炒粉,開心回家吸粉了!!!");
            }
        });

        thread.start();
        Thread.sleep(2000);
        obj = new Object();
        synchronized (Demo_park.class) {
            LockSupport.unpark(thread);
            System.out.println("落魄程序員做好了炒粉,通知客戶。");
        }
    }

運行結果如下:

同樣的,park和unpark加了synchronized關鍵字會造成死鎖。

2.4 總結

協作方式 加synchronized關鍵字 先喚醒後掛起
suspend/resume 死鎖 死鎖
wait/notify,notifyAll 不會死鎖 死鎖
park/unpark 死鎖 不會死鎖

 

 

 

 

 

 

 

 

 

 

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