Java Volatile关键字在int变量上使用时表现不符合预期
我相信你打算把你的年龄作为一个静态变量Java Volatile关键字在int变量上使用时表现不符合预期,java,multithreading,volatile,Java,Multithreading,Volatile,我相信你打算把你的年龄作为一个静态变量 Thread-0 :0:0 Thread-1 :1:0 Thread-2 :2:0 Thread-3 :3:0 Thread-4 :4:40 Thread-5 :5:0 Thread-6 :6:0 Thread-7 :7:0 Thread-8 :8:0 Thread-9 :9:0 您有age的实例副本,因此它不是共享副本,请使用static关键字使age类变为变量 我想,试试下面的代码吧 static volatile int age; 公共类神话扩展
Thread-0 :0:0
Thread-1 :1:0
Thread-2 :2:0
Thread-3 :3:0
Thread-4 :4:40
Thread-5 :5:0
Thread-6 :6:0
Thread-7 :7:0
Thread-8 :8:0
Thread-9 :9:0
您有age的实例副本,因此它不是共享副本,请使用static关键字使age类变为变量 我想,试试下面的代码吧
static volatile int age;
公共类神话扩展线程
{
int-num
import java.util.ArrayList;
}首先,您正在使用。你的意愿需要后者
其次,您不需要在代码中使用volatile关键字,因为您在其getter/setter上使用了已经同步的关键字
此外,如果您在修改arr时遇到同步问题,您应该使用一种机制来克服它,例如CopyOnWriteArrayList。下面是一些有问题的输出示例
static volatile int age;
MyTh(int k)
{
this.num = k;
}
@Override
public void run()
{
if (this.num == 4)
{
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :" + this.num + ":" + getAge());
}
synchronized int getAge()
{
return age;
}
synchronized void setAge(int k)
{
this.age = k;
}
public static void main(String[] args)
{
ArrayList arr = new ArrayList();
for (int i = 0; i < 10; i++)
arr.add(new MyTh(i));
for (int i = 0; i < 10; i++)
{
((Thread) arr.get(i)).start();
}
}
这里有两件事: 您正在使用一个实例变量age并创建一个神话类的10个实例,这样每个实例都有自己的副本,并且它们不会相互干扰操作。因此,根据@tanyehzheng的建议,您必须将其设置为静态,以便在所有10个实例中共享 您的问题是,如果我们有静电干扰,volatile将产生什么影响: static只声明副本将在所有实例之间共享,并且在多个线程尝试更新/访问它时,它不保证内存可见性。 另一方面,volatile将保证内存可见性,即如果变量发生任何写入,则所有后续读取都将看到最新值发生在关系之前。如果只使用静态,这是不可能的,因为CPU核心可能会缓存该值或将其放在寄存器中。 为什么在变量声明为volatile时对getter和setter使用synchronized关键字。这里不需要它。你只需要可见性,而不是原子性。所以像这样修改代码
import java.util.concurrent.CopyOnWriteArrayList;
class ThreadTest {
public static void main(String[] args) {
CopyOnWriteArrayList<Thread> arr = new CopyOnWriteArrayList<>();
for (int i =0; i<10 ; i++)
arr.add(new MyTh(i));
for (int i =0; i<10 ; i++) {
(arr.get(i)).start();
}
}
}
class MyTh extends Thread {
int num;
static /*volatile*/ int age;
MyTh (int k)
{this.num=k; }
@Override
public void run() {
if(this.num==4){
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :"+this.num + ":" + getAge());
}
synchronized int getAge() {
return age;
}
synchronized void setAge(int k) {
this.age =k;
}
}
首先将年龄变量标记为static,以便在类的所有实例之间共享该变量。在前面的示例中,它是一个实例字段,因此每个实例都有自己的副本。因此,一方所做的改变对另一方来说是不可见的
最新输出如下
public class MyTh extends Thread {
int num;
static volatile int age;
MyTh(int k)
{
this.num = k;
}
@Override
public void run() {
if (this.num == 4) {
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :" + this.num + ":" + getAge());
}
int getAge() {
return age;
}
void setAge(int k) {
age = k;
}
}
age在每个mythstatic中都是一个局部变量,但在每个类中都是一次,并且在每个实例中都有严格的单一副本。那么为什么要使用volatile呢?我只能继续使用static。虽然它在实例之间共享,但它可能会被实例缓存。可能不会反映对值的后续更新@tanyehzheng挥发物的使用在这方面是一种浪费example@snr取决于用例。因为它问的是volatile,我相信这两个同步的关键字被浪费了。嗨,我后来用了synchronized,只是为了检查这是否会有什么不同。但这不是必需的。为了理解volatile关键字在哪里使用,我做了一些修改,以便证明它的存在。但无法理解。我只使用了没有volatile的static,得到了Thread-0:0:0-Thread-1:1:0-Thread-2:0-Thread-4:4:40-Thread-3:3:40-Thread-5:5:40-6:40-Thread-7:40-8:40-Thread-9:9:40=>当static可以为我们做同样的事情时volatile有什么用途。@摘要volatile关键字强制每个线程更新他们在使用“标记”变量之前更改了该变量的值。这将确保使用最新的值。这就是为什么volatile对于正确的程序流至关重要的一个好例子。如何找到线程可能缓存静态变量副本的证据?。我打印hashcode值,以找出线程从何处读取静态变量。我发现所有的线都印了相同的值。和volatile.IMHO一样,它实际上是CPU级别的,所以不能通过java程序进行测试。而打印hashcode也无济于事。我建议你读一下马丁·汤普森的书
import java.util.concurrent.CopyOnWriteArrayList;
class ThreadTest {
public static void main(String[] args) {
CopyOnWriteArrayList<Thread> arr = new CopyOnWriteArrayList<>();
for (int i =0; i<10 ; i++)
arr.add(new MyTh(i));
for (int i =0; i<10 ; i++) {
(arr.get(i)).start();
}
}
}
class MyTh extends Thread {
int num;
static /*volatile*/ int age;
MyTh (int k)
{this.num=k; }
@Override
public void run() {
if(this.num==4){
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :"+this.num + ":" + getAge());
}
synchronized int getAge() {
return age;
}
synchronized void setAge(int k) {
this.age =k;
}
}
public class MyTh extends Thread {
int num;
static volatile int age;
MyTh(int k)
{
this.num = k;
}
@Override
public void run() {
if (this.num == 4) {
setAge(40);
}
System.out.println(Thread.currentThread().getName() + " :" + this.num + ":" + getAge());
}
int getAge() {
return age;
}
void setAge(int k) {
age = k;
}
}
Thread-0 :0:0
Thread-3 :3:0
Thread-2 :2:0
Thread-1 :1:0
Thread-4 :4:40
Thread-6 :6:40
Thread-8 :8:40
Thread-5 :5:40
Thread-7 :7:40
Thread-9 :9:40