一、前述
本文继续分享多线程的对象及变量的并发访问
二、实例变量非线程安全
平时开发中,经常会遇到多个线程共同访问一个实例变量,这样就会出现非线程安全的脏读问题
线程访问的对象中,如果有多个实例变量,那么运行的结果可能就是会出现交叉运行,如果只有一个实例变量,可能出现被后来的覆盖的现象
接下来看看 demo:
public class SynTestA {
private int num = 0;
public void addA(String name){
try {
if(name.equals("a")){
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b set over");
}
System.out.println(name + " num = " + num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class ThreadA extends Thread{
private SynTestA synTestA;
public ThreadA(SynTestA synTestA){
super();
this.synTestA = synTestA;
}
@Override
public void run(){
super.run();
synTestA.addA("a");
}
}
public class ThreadB extends Thread{
private SynTestA synTestA;
public ThreadB(SynTestA synTestA){
super();
this.synTestA = synTestA;
}
@Override
public void run(){
super.run();
synTestA.addA("b");
}
}
public class Run {
public static void main(String[] args) {
SynTestA synTestA = new SynTestA();
ThreadA threadA = new ThreadA(synTestA);
threadA.start();
ThreadB threadB = new ThreadB(synTestA);
threadB.start();
}
}
运行结果:
a set over
b set over
b num = 200
a num = 200
上述代码就是两个线程同时一个没有同步的方法,两个线程并发同时对业务对象中的实例变量 num 操作的时候,就出现了非线程安全的问题,这里原本应该是 a set over,a num = 100,但是由于并发,a 线程跑到一半的时候,b 线程进入了这个方法,并且将实例变量 num 修改为了 200,b 线程结束以后,a 还在继续跑,此时读取到了 num = 200,所以就出现了所谓的数据脏读,要使程序按照正确的逻辑运行,只需要对方法 addA 前面加上关键字 synchronized 就可以保证不出现数据脏读,如下:
public class SynTestA {
private int num = 0;
public synchronized void addA(String name){
try {
if(name.equals("a")){
num = 100;
System.out.println("a set over");
Thread.sleep(2000);
}else{
num = 200;
System.out.println("b set over");
}
System.out.println(name + " num = " + num);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
a set over
a num = 100
b set over
b num = 200
将方法改为同步以后,两个线程访问同一个对象中的同步方法时一定是线程安全的,同步访问,所以先打印 a ,再打印 b