Java多線程編程核心技術---對象及變量的併發訪問

「非線程安全」其實會在多個線程對同一個對象中的實例變量進行併發訪問時發生,產生的後果就是「髒讀」,也就是渠道的數據實際上是被更改過的。而「線程安全」就是得到的實例變量的值是通過同步處理的,不會出現髒讀現象。java

  • 方法內的變量爲線程安全

「非線程安全」問題存在於「實例變量」中,若是是方法內部的私有變量,則不存在「非線程安全」問題,所得的結果也就是「線程安全」的了。安全

import java.util.HashMap;
import java.util.Map;

public class HasSelfPrivateNum {
	//private  int num = 0;

    public void add(String username){
        Map<Integer,Integer> m = new HashMap<Integer,Integer>();
        try {
            int num = 0;
            if (username.equals("a")){
                num = 100;
                System.out.println("a set over");
                Thread.sleep(1000);
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("A賦值前--K: "+mm.getKey()+",V: "+mm.getValue());
                for(int i=0;i<1000;i++){
                    m.put(i, i+10);
                }
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("A賦值後--K: "+mm.getKey()+",V: "+mm.getValue());
            }else {
                num = 200;
                System.out.println("b set over");
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("B賦值前--K: "+mm.getKey()+",V: "+mm.getValue());
                for(int i=0;i<1000;i++){
                    m.put(i, i+20);
                }
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("B賦值後-K: "+mm.getKey()+",V: "+mm.getValue());
            }
            System.out.println("username=" + username + ", num=" + num);

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

線程a併發

public class ThreadA extends Thread {
    private HasSelfPrivateNum numRef;
    public ThreadA(HasSelfPrivateNum numRef){
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.add("a");
    }
}

線程bide

public class ThreadB extends  Thread{
    private HasSelfPrivateNum numRef;
    public ThreadB(HasSelfPrivateNum numRef){
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.add("b");
    }
}

執行主線程this

public class Run {
    public static void main(String[] args) {
        HasSelfPrivateNum numRf = new HasSelfPrivateNum();
        ThreadA threadA = new ThreadA(numRf);
        threadA.start();
        ThreadB threadB = new ThreadB(numRf);
        threadB.start();
    }
}

結果以下:spa

a set over
b set over
B賦值後-K: 0,V: 20
B賦值後-K: 1,V: 21
B賦值後-K: 2,V: 22
B賦值後-K: 3,V: 23
B賦值後-K: 4,V: 24
B賦值後-K: 5,V: 25
B賦值後-K: 6,V: 26
B賦值後-K: 7,V: 27
B賦值後-K: 8,V: 28
B賦值後-K: 9,V: 29
B賦值後-K: 10,V: 30
B賦值後-K: 11,V: 31
B賦值後-K: 12,V: 32
****
username=b, num=200
A賦值後--K: 0,V: 10
A賦值後--K: 1,V: 11
A賦值後--K: 2,V: 12
A賦值後--K: 3,V: 13
A賦值後--K: 4,V: 14
A賦值後--K: 5,V: 15
A賦值後--K: 6,V: 16
A賦值後--K: 7,V: 17
A賦值後--K: 8,V: 18
A賦值後--K: 9,V: 19
A賦值後--K: 10,V: 20
A賦值後--K: 11,V: 21
A賦值後--K: 12,V: 22
A賦值後--K: 13,V: 23
A賦值後--K: 14,V: 24
A賦值後--K: 15,V: 25
A賦值後--K: 17,V: 27
  • 實例變量非線程安全

若是多個線程共同訪問一個對象中的實例變量,則有可能出現「非線程安全」問題。用線程訪問的對象中若是有多個實例變量,則運行的結果有可能出現交叉的狀況。
將上面的代碼作以下更改:線程

import java.util.HashMap;
import java.util.Map;

public class HasSelfPrivateNum {
	private  int num = 0;
    Map<Integer,Integer> m = new HashMap<Integer,Integer>();
    public void add(String username){
        try {
//            int num = 0;
            if (username.equals("a")){
                num = 100;
                System.out.println("a set over");
                Thread.sleep(1000);
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("A賦值前--K: "+mm.getKey()+",V: "+mm.getValue());
                for(int i=0;i<1000;i++){
                    m.put(i, i+10);
                }
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("A賦值後--K: "+mm.getKey()+",V: "+mm.getValue());
            }else {
                num = 200;
                System.out.println("b set over");
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("B賦值前--K: "+mm.getKey()+",V: "+mm.getValue());
                for(int i=0;i<1000;i++){
                    m.put(i, i+20);
                }
                for(Map.Entry<Integer, Integer> mm : m.entrySet())
                    System.out.println("B賦值後-K: "+mm.getKey()+",V: "+mm.getValue());
            }
            System.out.println("username=" + username + ", num=" + num);

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

運行結果以下:code

a set over
b set over
B賦值後-K: 0,V: 20
B賦值後-K: 1,V: 21
B賦值後-K: 2,V: 22
B賦值後-K: 3,V: 23
B賦值後-K: 4,V: 24
B賦值後-K: 5,V: 25
B賦值後-K: 6,V: 26
B賦值後-K: 7,V: 27
B賦值後-K: 8,V: 28
B賦值後-K: 9,V: 29
B賦值後-K: 10,V: 30
****
username=b, num=200
A賦值前--K: 0,V: 20
A賦值前--K: 1,V: 21
A賦值前--K: 2,V: 22
A賦值前--K: 3,V: 23
A賦值前--K: 4,V: 24
A賦值前--K: 5,V: 25
A賦值前--K: 6,V: 26
A賦值前--K: 7,V: 27
A賦值前--K: 8,V: 28
A賦值前--K: 9,V: 29
A賦值前--K: 10,V: 30
A賦值前--K: 11,V: 31
*****
A賦值前--K: 966,V: 986
A賦值前--K: 965,V: 985
A賦值前--K: 964,V: 984
A賦值後--K: 0,V: 10
A賦值後--K: 1,V: 11
A賦值後--K: 2,V: 12
A賦值後--K: 3,V: 13
A賦值後--K: 4,V: 14
A賦值後--K: 5,V: 15
A賦值後--K: 6,V: 16
A賦值後--K: 7,V: 17
****
A賦值後--K: 960,V: 970
A賦值後--K: 967,V: 977
A賦值後--K: 966,V: 976
A賦值後--K: 965,V: 975
A賦值後--K: 964,V: 974
username=a, num=200
相關文章
相關標籤/搜索