Javaweb之線程2

volatile

import java.util.Scanner;

public class ThreadDemo {
    static class Counter {
        public int flag = 0;
    }
    public static void main(String[] args) {
        Counter counter = new Counter();
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (counter.flag == 0) {

                }
                System.out.println("循環結束");
            }
        };
        t1.start();
        Thread t2 = new Thread() {
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                System.out.println("親輸入一個整數");
                counter.flag = scanner.nextInt();


            }
        };
        t2.start();
    }
}

在這裏插入圖片描述
這個代碼本來的預期效果是輸入整數後循環結束,但是當輸入後,並沒有結束。

其實,這是編譯器優化的問題。開始,線程1 反覆對循環條件進行比較操作(先從內存中讀取flag的值到CPU,在CPU中比較這個值和0的相等關係),因爲從CPU寄存器上讀取數據比內存中讀數據還是快很多的,編譯器判定這個判斷相等操作就是不斷從內存中讀取內存而已,於是編譯器就進行了優化,在第一次吧數據從內存讀取到CPU後,之後就直接從CPU中讀取數據,所以編譯器認爲flag沒有改動,就出現了誤判。

這樣的優化策略就是“內存可見性”;
如果優化生效,內存不可見,不生效,內存纔是可見的。
在這裏插入圖片描述
在這裏插入圖片描述
但是當在flag前加上volatile後,就可以保證內存可見性了。

但這個和原子性是沒有關係的,對於一個線程讀,一個線程寫,得用volatile解決,但是對於兩個線程修改同一變量時,還得用加鎖去解決。

notify和wait

對象等待集 (本質讓程序員有一定手段去幹預線程調度)
主要還是因爲搶佔式執行的問題

wait方法:當操作條件不成熟就等待(必須在sychronized內部使用)
notify方法:當條件成熟,通知指定線程來工作。(也必須在sychronized裏使用)

wait步驟:
1.釋放鎖
2.等待通知
3.收到通知,重新獲取鎖,繼續往下執行
其中,前兩步在wait中是原子的,避免競態條件問題。
在這裏插入圖片描述
調用鎖的對象和wait的對象必須是對應的。

import java.util.Scanner;

public class ThreadDemo4 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread() {
            @Override
            public void run() {
                synchronized (locker) {
                    while (true) {
                        try {
                            System.out.println("wait開始");
                            locker.wait();
                            System.out.println("wait結束");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        };
        t1.start();
        Thread t2 = new Thread() {
            @Override
            public void run() {
                Scanner scanner = new Scanner(System.in);
                System.out.println("輸入整數");
                int num = scanner.nextInt();
                synchronized (locker) {
                    System.out.println("notify開始");
                    locker.notify();
                    System.out.println("notify結束");
                }
            }
        };
        t2.start();
    }
}

在這裏插入圖片描述

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