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");
    }
}

线程b

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");
    }
}

执行主线程

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();
    }
}

结果如下:

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();
        }
    }
}

运行结果如下:

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

猜你喜欢

转载自my.oschina.net/u/1986568/blog/1584461