其实今天主要想说的是volatile的读写问题,首先来说一下volatile能解决什么问题,不能解决什么问题。
1. volatile能够解决线程中数据读取不一致的问题,我们知道每一个线程都有自己的线程栈,自己在自己的线程栈中存放自己线程中所需要的变量,其实可以理解成计算机硬件中的寄存器,目的是为了减少自己对内存的访问次数,那么线程栈中什么时候进行更新呢?这在线程中称为可见性,下面我们举一个错误的例子来证明线程中的数据修改的不可见性。
public class Test extends Thread{
public boolean mark = false;
public int i = 0;
public void run(){
while(!mark){
i++;
}
}
public static void main(String[] args0) throws InterruptedException{
Test test = new Test();
test.start();
Thread.sleep(2000);
test.mark = true;
System.out.println(test.i);
}
}
上面的例子中,我们实例化了一个线程,并且在这个线程中对mark进行检测,并对i进行++操作。然后我们将主线程休眠2s,然后对mark的值进行更改,我们希望通过这样的更改来停止线程,但是事实是我们并没有让test线程停止。这就是上面说的线程使用了自己内存中的寄存器,我们修改的主存中的数据。
如果使用了volatile效果就完全不一样,volatile的目的是为了让我们能够获取到内存中最新的数据,那么我们在更改数据的时候对于其他线程来说就是可见的。上面的例子只要把标志位添加volatile属性即可。
public class Test extends Thread{
public volatile boolean mark = false;
public int i = 0;
public void run(){
while(!mark){
i++;
}
}
public static void main(String[] args0) throws InterruptedException{
Test test = new Test();
test.start();
Thread.sleep(2000);
test.mark = true;
System.out.println(test.i);
}
}
public class Counter {
public volatile static int count = 0;
public static void inc() {
//这里延迟1毫秒,使得结果明显
try {
Thread.sleep(1);
} catch (InterruptedException e) {
}
count++;
}
public static void main(String[] args) {
//同时启动1000个线程,去进行i++计算,看看实际结果
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter.inc();
}
}).start();
}
//这里每次运行的值都有可能不同,可能为1000
System.out.println("运行结果:Counter.count=" + Counter.count);
}
}
其实我们新建了1000个线程,每个线程对变量count进行++ 操作,我们希望得到的值是1000,但是并不全是。这是为什么?因为我们在进行操作的时候只是使用volatile来保证了每个线程都能够读取到内存中最新的数据,但是我们并不能避免两个线程A,B获取到的count值是相同的,如果两个线程是相同的,那么就必定会存在这样的错误。个人觉得这样的问题可以通过线程锁来进行限制。个人理解。