Java 關於執行緒的一些使用

瞌睡先生想睡覺發表於2018-06-11

public class Demo {

    public static void main(String[] agr) throws Exception {
        Demo demo = new Demo();
        demo.test();
    }


    //執行緒的wait,notify必須和synchronized一起使用
    //即,當前執行緒拿屬於object的鎖之後,就可以呼叫wait讓此執行緒進入等待狀態,然後丟擲鎖,等待其他執行緒那到object的鎖之後喚醒
    //掛在object上的執行緒,如果有多個執行緒掛在object一個物件上那麼notify只會喚醒一個執行緒
    public Object object = new Object();

    public void test() throws InterruptedException {

        new Thread() {
            @Override
            public void run() {
                synchronized (object) {
                    L.d("執行緒1進入等待狀態");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    L.d("執行緒1繼續執行");
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                synchronized (object) {
                    L.d("執行緒2進入等待狀態");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    L.d("執行緒2繼續執行");
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                synchronized (object) {
                    L.d("執行緒3進入等待狀態");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    L.d("執行緒3繼續執行");
                }
            }
        }.start();

        Thread.sleep(1000);
        synchronized (object) {
            object.notify();
        }
    }

}

如果像下面這個沒有拿到鎖就調起wait就會丟擲IllegalMonitorStateException異常

        new Thread() {
            @Override
            public void run() {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

或者是這樣,雖然拿到了object的鎖但是並沒有掛起在object這個物件上就也會丟擲異常

        new Thread() {
            @Override
            public void run() {
                synchronized (object){
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }.start();

上面兩種直接呼叫的wait是將自己直接掛在Thread本類這個物件上所以要進行拿到本類物件的鎖才可以,像下面這樣就不會丟擲異常

    public void test() throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public synchronized void run() {
                L.d("執行緒開始");
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                L.d("執行緒完成等待");
            }
        };

        thread.start();

        Thread.sleep(1000);
        synchronized (thread) {
            thread.notify();
        }
    }

有關執行緒的狀態

NEW 剛剛建立的執行緒尚且沒有執行
RUNNABLE 正在執行中的執行緒
WAITING 執行緒等待中
TIMED_WAITING 計時等待,呼叫wait(long timeout)之後所處的狀態
BLOCKED 阻塞狀態,synchronized的鎖已經被其他執行緒拿到而沒有釋放,此時執行緒被阻塞在synchronized
TERMINATED 終止狀態,執行緒執行完成

另外如果執行緒呼叫sleep是不會釋放已經拿到的synchronized鎖,如果是呼叫wait方法則會釋放已經到的synchronized鎖

關於thread.join()方法的使用,如果在a執行緒中呼叫了b執行緒的join方法,那麼a執行緒就會進入等待直至b執行緒執行完成才會繼續執行,用網上的專業一點的說法就是將執行緒由並行轉為序列,join內部呼叫的就是wait方法,

例子:

    public void test() {
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    L.d("執行緒1 : " + i);
                }
            }
        };
        thread1.start();

        Thread thread2 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    L.d("執行緒2 : " + i);
                    try {
                        if (i == 5) {
                            thread1.join();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread2.start();
    }

join方法的原始碼:

    public final synchronized void join(long millis) throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

相關文章