1. 程式人生 > >java多執行緒——執行緒之間的可見性

java多執行緒——執行緒之間的可見性

目錄

一、簡介

一、簡介

       我們知道執行緒在工作的時候有自己的私有記憶體,工作記憶體。程式執行的時候從主記憶體拉取需要的變數到工作記憶體,處理完再返回主記憶體。這篇文章總結哪些程式碼會使執行緒去主記憶體拉取變數。

二、volatile

       volatile修飾的變數,不論什麼語句都會從主記憶體拉取變數。

public class Test01 {

    public volatile boolean b = true;

    public void m() {
        System.out.println("start");
        while(this.b){

        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

該程式能順利完成,不會死迴圈。

三、synchronized

呼叫其他synchronized加鎖的程式碼塊,系統會從主記憶體拉取變數

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            synchronized(lock) {

            }
        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

這種情況同樣不會死迴圈。

注意,synchronized加鎖後用到的變數才會從主記憶體拉取。

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        synchronized(lock) {
            while(this.b){

            }
        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

這種情況,程式會死迴圈。 

四、不會從主記憶體拉取的操作

只要不影響到變數的值,變數不參與運算,則不會從主記憶體中拉取,比如while,if,賦值給其他變數,作為方法引數等

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            doSomething();
            if(b) {

            }
            boolean value = b;
        }
        System.out.println("end");
    }

    public void doSomething() {

    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

這種情況,程式會死迴圈。

如果呼叫了被synchronized加鎖的方法,或者方法中有被synchronized加鎖的程式碼塊,那麼變數將會從主記憶體中拉取。

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            doSomething();
        }
        System.out.println("end");
    }

    public void doSomething() {
        synchronized(lock) {

        }
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

呼叫的synchronized加鎖的方法即使鎖的是毫不相干的物件,同樣會使得變數從主記憶體拉取

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            System.out.println(this.b);
        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

這裡的System.out.println鎖的是PrintStream的例項,更是毫不相關

這種情況,程式不會死迴圈。 

五、從主記憶體中拉取的操作

變數被賦值,變數參與計算,比如加減乘除,字串拼接,++,--等操作,執行緒休眠會使從主記憶體中拉取變數。

public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}
public class Test01 {

    public boolean b = true;

    public Object lock = new Object();

    public void m() {
        System.out.println("start");
        while(this.b){
            String str = b + "";
        }
        System.out.println("end");
    }

    public static void main(String[] args) {
        Test01 test = new Test01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                test.m();
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("set");
        test.b = false;
    }

}

這種情況,程式不會死迴圈