“非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”,也就是渠道的数据其实是被更改过的。而“线程安全”就是获得的实例变量的值是经过同步处理的,不会出现脏读现象。
-
方法内的变量为线程安全
“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题,所得的结果也就是“线程安全”的了。
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