java使用輪詢和wait()/notify()實現多線程之間的通信

使用輪詢方式實現通信:

public class MyList {
    private static int i = 0;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        MyList.i = i;
    }

    private volatile List<String> list = new ArrayList<String>();
        public void add() {
            list.add("elements");
        }
    public int size() {
        return list.size();
    }
}
public class ThreadA implements Runnable{
    private MyList list;
    public ThreadA(MyList list) {
        this.list = list;
    }
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                list.add();
                System.out.println(Thread.currentThread().getName()+"添加了" + (i + 1) + "個元素");
                Thread.sleep(2000);
                if(list.getI()!= 0){
                    break;
                }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}
public class ThreadB implements Runnable{
    private MyList list;
        public ThreadB(MyList list) {
        this.list = list;
    }
    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"判斷當前容量。");
            if (list.size() == 5) {
                System.out.println("線程b準備退出了");
                list.setI(list.size());
                break;
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
        MyList service = new MyList();
        Thread thread1 = new Thread(new ThreadA(service),"A");
        Thread thread2 = new Thread(new ThreadB(service),"B");
        thread1.start();
        thread2.start();
    }
}
在這種方式下,線程A不斷地改變條件,線程ThreadB不停地通過while語句檢測這個條件(list.size()==5)是否成立 ,從而實現了線程間的通信。但是這種方式會浪費CPU資源。之所以說它浪費資源,是因爲JVM調度器將CPU交給線程B執行時,它沒做啥“有用”的工作,只是在不斷地測試 某個條件是否成立。就類似於現實生活中,某個人一直看着手機屏幕是否有電話來了,而不是: 在幹別的事情,當有電話來時,響鈴通知TA電話來了。
注意MyList中變量list是否爲volatile的區別。詳見:volatile關鍵字的初步理解
使用wait()/notify()方式實現通信: 

public class MyList {
    private static volatile List<String> list = new ArrayList<String>();

    public static void add() {
        list.add("anyString");
    }

    public static int size() {
        return list.size();
    }
}
public class ThreadA extends Thread{
    private Object lock;

    public ThreadA(Object o) {
        this.lock = o;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                if (MyList.size() < 5) {
                    System.out.println(Thread.currentThread().getName()+"wait begin "
                            + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    lock.wait();
                    System.out.println(Thread.currentThread().getName()+"wait end  "
                            + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadB extends Thread{
    private Object lock;

    public ThreadB(Object o) {
        this.lock = o;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                for (int i = 0; i < 10; i++) {
                    MyList.add();
                    System.out.println(Thread.currentThread().getName()+"添加了" + (i + 1) + "個元素!");
                    if (MyList.size() == 5) {
                        lock.notify();
                        System.out.println(Thread.currentThread().getName()+"已經發出了通知");
                    }
                    if(MyList.size() > 5){
                        System.out.println("你要執行嗎?");
                        Thread.yield();
                    }
                    Thread.sleep(500);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Main {
    public static void main(String[] args) {
        try {
            Object lock = new Object();
            ThreadA a = new ThreadA(lock);
            ThreadB b = new ThreadB(lock);

            a.start();
            Thread.sleep(50);
            b.start();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

線程A要等待某個條件滿足時(list.size()==5),才執行操作。線程B則向list中添加元素,改變list 的size。

A,B之間如何通信的呢?也就是說,線程A如何知道 list.size() 已經爲5了呢?

這裏用到了Object類的 wait() 和 notify() 方法。

當條件未滿足時(list.size() !=5),線程A調用wait() 放棄CPU,並進入阻塞狀態。---不像while輪詢那樣佔用CPU

當條件滿足時,線程B調用 notify()通知 線程A,所謂通知線程A,就是喚醒線程A,並讓它進入可運行狀態。

這種方式的一個好處就是CPU的利用率提高了。

但是也有一些缺點:比如,線程B先執行,一下子添加了5個元素並調用了notify()發送了通知,而此時線程A還執行;當線程A執行並調用wait()時,那它永遠就不可能被喚醒了。因爲,線程B已經發了通知了,以後不再發通知了。這說明:通知過早,會打亂程序的執行邏輯。

我以爲notify之後處於wait狀態的代碼會立即執行,至少在yield之後會立即執行,但是昨天問了一位同事之後,說是要等同步代碼塊執行完之後纔會釋放鎖讓處於wait的線程執行。






發佈了80 篇原創文章 · 獲贊 25 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章