前两天一个开发同事问我,为什么线程A创建的变量在线程B读取不到?这个问题就设计到线程之间变量作用域的问题了。
在Java中,变量作用域通常指的是变量在代码中的可访问范围,如,方法内可见,类中可见和全局可见,如果一个变量在某个作用域内声明,那么只有在这个作用域内或者嵌套的作用域内才能访问这个变量。在多线程编程中,作用域问题可能导致一个线程无法访问另一个线程中定义的变量。
下面是一个的例子,演示了变量作用域问题如何影响多线程程序中的变量,如下代码:
public class ScopeExample {
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
int localVariableInThread1 = 42; // 线程1的局部变量
System.out.println("Thread 1: localVariableInThread1 = " + localVariableInThread1);
}
});
Thread thread2 = new Thread(() -> {
// 这里无法访问localVariableInThread1,因为它是在thread1的作用域内定义的
// 下面的代码会导致编译错误:
// int copiedValue = localVariableInThread1; // 错误:找不到符号
System.out.println("Thread 2 is running, but cannot access localVariableInThread1");
});
thread1.start();
thread2.start();
}
}
在这个例子中,localVariableInThread1
是在 thread1
的 run
方法内部定义的局部变量,由于它是局部变量,它的作用域仅限于 run
方法内部,因此,当 thread2
尝试访问 localVariableInThread1
时,它无法访问的,因为这个变量对 thread2
是不可见的。
要解决这个问题,可以使用类变量或者全局变量,例如,将其定义为类的字段(静态或非静态),这样所有的线程都可以访问它,如下代码:
public class SharedScopeExample {
private static int sharedVariable = 42; // 静态字段,对所有线程可见
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1: sharedVariable = " + sharedVariable);
// 可以修改sharedVariable的值
sharedVariable = 99;
});
Thread thread2 = new Thread(() -> {
// 稍微延迟,以便thread1有机会修改sharedVariable的值
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread 2: sharedVariable = " + sharedVariable); // 可以访问sharedVariable的值
});
thread1.start();
thread2.start();
}
}
在这个修改后的例子中,sharedVariable
是一个静态字段,对所有线程都是可见的。因此,thread1
和 thread2
都可以访问和修改它的值。
其它知识参考(来自网络CV),如下:
JMM,Java内存模型概念,它规定了内存主要划分为主内存和工作内存两种,主内存和工作内存与JVM内存划分(堆、栈、方法区)在不同的层次上,如果非要对应,主内存对应的是Java堆中的对象实例部分,工作内存对应的是栈中的部分区域,从更底层来说,主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存。
此外,JMM还定义了线程和主内存之间的抽象关系,线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本,而且,JMM还规定所有的共享变量都存储于主内存,这里的共享变量指的是实例变量和类变量,不包含局部变量,因为局部变量是线程私有的,因此不存在竞争问题。