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