局部变量的可见性
1 问题的引出
局部变量跨线程,又不能用volatile,怎么保证其可见性
2 是否真的能有局部变量跨线程写入?
看一下这段代码:
public static void main(String []f) {
Integer integer = 1;
new Thread(new InnerThread(integer)).start();
while(true) {
if(integer == 2) break;
}
}
private static class InnerThread implements Runnable {
private Integer innerInteger;
public InnerThread(Integer integer) {
this.innerInteger = integer;
}
@Override
public void run() {
innerInteger = 2;
}
}
这个代码有没有可见性问题?答案是没有的
两条线程访问的其实是2个变量,是没有线程并发可见性问题的,因为java不像c++那样有变量的引用语法,&integer,子线程的构造其实是c++里面的值传递而不是引用传递,什么叫值传递?引用传递?
public static void main(String []f) {
Integer x = 1;
setInt(x);
System.out.println(x);
}
private static void setInt(Integer x) {
x = 2;
}
打印1,值传递,打印2,引用传递
子线程写入的是线程内部的局部对象,主线程读取的是main方法的局部对象,根本是2个变量,自然无可见性问题
3 那么有没有使用一个局部对象的多线程写入?答案是有的
public static void main(String []f) {
Integer integer = 1;
new Thread(new Runnable() {
@Override
public void run() {
integer = 2;
}
}).start();
while(true) {
if(integer == 2) break;
}
}
然而,这个代码是过不了编译的,必须在integer上加上final
public static void main(String []f) {
final Integer integer = 1;
new Thread(new Runnable() {
@Override
public void run() {
integer = 2;
}
}).start();
while(true) {
if(integer == 2) break;
}
}
然而加上final后,integer=2自然非法了
所以归根结底jdk从语法上避免了这个问题的发生
5 扩展思考
public class TestMain {
private volatile TestC testC;
private static class TestC {
private Integer integer;
public Integer getInteger() {
return integer;
}
public void setInteger(Integer integer) {
this.integer = integer;
}
}
}
这个代码能保证integer的可见性吗,答案是不能,volatile修饰的是testC,而不是integer,这个性质有点像Unsafe控制ConcurrentHashMap内并发数组元素的可见性 ,数组对象volatile,数组元素没有volatile,数组元素一样会有可见性问题
6
public static void main(String []f) {
TestC testC = new TestC();
testC.setInteger(1);
new Thread(new Runnable() {
@Override
public void run() {
testC.setInteger(2);
}
}).start();
while(true) {
if(testC.getInteger() == 2) break;
}
}
private static class TestC {
private Integer integer;
public Integer getInteger() {
return integer;
}
public void setInteger(Integer integer) {
this.integer = integer;
}
}
(请注意这个地方的setInteger,是否多线程可见未知,这是极不严谨缺乏稳定性的写法)
这个代码是否实现了第2点所说的“局部变量跨线程写入”?
对不起,这个不属于写入,这里的“写入”指的是,让共享变量作为=的左值,此之谓“写入”,比如:
public static void main(String []f) {
TestC testC = new TestC();
testC.setInteger(1);
new Thread(new Runnable() {
@Override
public void run() {
TestC tmp = new TestC();
tmp.setInteger(2);
testC = tmp;
}
}).start();
while(true) {
if(testC.getInteger() == 2) break;
}
}
private static class TestC {
private Integer integer;
public Integer getInteger() {
return integer;
}
public void setInteger(Integer integer) {
this.integer = integer;
}
}
此时又会报,要加final的提示
我们得到重要结论:局部变量永远不能被多线程写入,既然无法多线程写入,自然也没有可见性问题
浙公网安备 33010602011771号