在 ThreadLocal 实例中获取 volatile 变量的目的
Purpose of taking volatile variable in ThreadLocal instance
这是在一次技术讲座中向我提出的问题之一。
public class MyClass {
volatile int i;
}
我将上面的 MyClass 用作 ThreadLocal,那么在其中使用任何 volatile 变量有什么用?
final ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();
如果该实例仅与 ThreadLocal
一起使用并且没有传递,那么 volatile
似乎没有多大意义。
但是请注意,MyClass
只是一个 class 和其他任何东西一样:没有什么可以阻止它在不涉及线程本地存储的上下文中使用。在这些情况下,变量 volatile
.
很有意义
Java 中的 volatile 关键字保证 volatile 变量的值总是从主内存中读取,而不是从线程的本地缓存中读取。
在某些情况下,Java 可以使用可变变量作为实现同步的替代方法,例如可见性。使用 volatile 变量可以保证一旦写操作完成,所有 reader 线程都会看到 volatile 变量的更新值,没有 volatile 关键字,不同的 reader 线程可能会看到不同的值。让我们通过一个例子来理解这一点。
public class C {
private volatile int COUNTER=0;
public static void main(String... q)
{
new C().call();
}
public void call()
{
new A().start();
new B().start();
}
class A extends Thread
{
public void run()
{
int i =0;
while(i<100)
{
COUNTER++;
System.out.println(" A COUNTER : "+COUNTER+" *** "+"i : "+i);
try{
Thread.sleep(50);
}
catch(Exception e)
{
}
i++;
}
}
}
class B extends Thread
{
public void run()
{
int i =0;
while(i<100)
{
COUNTER++;
System.out.println(" B COUNTER : "+COUNTER+" *** "+"i : "+i);
try{
Thread.sleep(50);
}
catch(Exception e)
{
}
i++;
}
}
}
}
我在两个线程之间共享了 COUNTER 变量,并使用了 volatile 而不是同步,这样更新的值将在主内存中全局共享,其他线程将获得更新的值。
只需替换上面的代码再试一次:
private volatile int COUNTER=0;
和
private int COUNTER=0;
重新运行程序并注意区别。
public class ThreadLocal
This class provides thread-local variables. These variables differ
from their normal counterparts in that each thread that accesses one
(via its get or set method) has its own, independently initialized
copy of the variable.
The Java programming language allows threads to access shared
variables (§17.1). As a rule, to ensure that shared variables are
consistently and reliably updated, a thread should ensure that it has
exclusive use of such variables by obtaining a lock that,
conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile
fields, that is more convenient than locking for some purposes.
因此,在您的示例中,每个线程都有自己的 MyClass 实例。如果是唯一使用MyClass,允许并发访问i
。
volatile 是 "more convenient than locking" 机制的关键字 "enforces mutual exclusion"。
因此,您不需要在此代码中使用 volatile。
此外,它会降低性能。
这是在一次技术讲座中向我提出的问题之一。
public class MyClass {
volatile int i;
}
我将上面的 MyClass 用作 ThreadLocal,那么在其中使用任何 volatile 变量有什么用?
final ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();
如果该实例仅与 ThreadLocal
一起使用并且没有传递,那么 volatile
似乎没有多大意义。
但是请注意,MyClass
只是一个 class 和其他任何东西一样:没有什么可以阻止它在不涉及线程本地存储的上下文中使用。在这些情况下,变量 volatile
.
Java 中的 volatile 关键字保证 volatile 变量的值总是从主内存中读取,而不是从线程的本地缓存中读取。
在某些情况下,Java 可以使用可变变量作为实现同步的替代方法,例如可见性。使用 volatile 变量可以保证一旦写操作完成,所有 reader 线程都会看到 volatile 变量的更新值,没有 volatile 关键字,不同的 reader 线程可能会看到不同的值。让我们通过一个例子来理解这一点。
public class C {
private volatile int COUNTER=0;
public static void main(String... q)
{
new C().call();
}
public void call()
{
new A().start();
new B().start();
}
class A extends Thread
{
public void run()
{
int i =0;
while(i<100)
{
COUNTER++;
System.out.println(" A COUNTER : "+COUNTER+" *** "+"i : "+i);
try{
Thread.sleep(50);
}
catch(Exception e)
{
}
i++;
}
}
}
class B extends Thread
{
public void run()
{
int i =0;
while(i<100)
{
COUNTER++;
System.out.println(" B COUNTER : "+COUNTER+" *** "+"i : "+i);
try{
Thread.sleep(50);
}
catch(Exception e)
{
}
i++;
}
}
}
}
我在两个线程之间共享了 COUNTER 变量,并使用了 volatile 而不是同步,这样更新的值将在主内存中全局共享,其他线程将获得更新的值。
只需替换上面的代码再试一次:
private volatile int COUNTER=0;
和
private int COUNTER=0;
重新运行程序并注意区别。
public class ThreadLocal
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable.
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields, that is more convenient than locking for some purposes.
因此,在您的示例中,每个线程都有自己的 MyClass 实例。如果是唯一使用MyClass,允许并发访问i
。
volatile 是 "more convenient than locking" 机制的关键字 "enforces mutual exclusion"。
因此,您不需要在此代码中使用 volatile。
此外,它会降低性能。