Java 如何在线程之间共享对象以证明它不是线程安全的?
MutableInteger不被认为是线程安全类,因为当从不同线程调用时,它可能会返回过时的值Java 如何在线程之间共享对象以证明它不是线程安全的?,java,multithreading,Java,Multithreading,MutableInteger不被认为是线程安全类,因为当从不同线程调用时,它可能会返回过时的值 class MutableInteger{ private int value; public int get(){ return value; } public void set(int value){ this.value=value; } } 这是我的线程实现:-有一个主线程(main方法)并从中调用MyThread
class MutableInteger{
private int value;
public int get(){
return value;
}
public void set(int value){
this.value=value;
}
}
这是我的线程实现:-有一个主线程(main方法)并从中调用MyThread
class MyThread extends Thread{
private int num;
public void set(int num){
this.num=num;
}
public void run(){
MutableInteger mi=new MutableInteger();
mi.set(num);
System.out.println("Mythread "+mi.get());
}
}
class MutableIntegerTest{
public static void main(String[] args){
MyThread t1=new MyThread();
t1.set(10);
t1.start();
MutableInteger mi=new MutableInteger();
mi.set(99);
System.out.println("Main thread" +mi.get());
}
}
这就是我未能理解的。对于访问状态变量value
我们需要使用类MutableInteger
的getter和setter方法,因此对于每次访问,我们都需要实例化MutableInteger
类。现在根据理论,每个对象都有自己的实例变量副本。在我的实现中,两个线程仅通过MutableInteger
实例访问值
变量。在我的例子中,值
在线程之间不共享,从而使我的MutableInteger线程安全
我应该如何使线程共享值
?我是否应该公开该对象,以便在两个线程中使用相同的对象?但是这不会将对象公开给所有可用的类和线程吗
我做错了什么吗?如果您想测试
可变整数
以亲眼看到它不是线程安全的,您必须在线程之间共享一个对象。如果每个线程都创建了自己的instancefMutableInteger
,那么正如您所注意到的,您实际上并没有测试这个特定的东西,因为它只被创建它的单个线程访问
因此,您需要创建一个实例,并让两个线程都使用它。例如:
可运行对象(最好使用扩展线程的类):
现在,如果mutableInteger是线程安全的,那么您希望结果总是打印109000。但当然不会。每次运行时,它可能会打印不同的数字,有时甚至会为不同的线程打印不同的数字。您当前的代码是线程安全的,但一般来说,您的类不是线程安全的。在t1.start()
之前使用t1.set()
是安全的,因为线程启动发生在edge之前(从启动的线程内部,您可以看到在thread.start()
调用之前启动线程中发生的所有事件)。但是,如果在线程启动后调用t1.set()
,则不能保证线程会看到该新值
对于MutableInteger
也一样。目前,线程之间不共享MutableInteger
实例,因此它们工作正常。但是,如果您共享,则不能保证更改是可见的。例如,考虑< <代码>运行()>代码>方法:
public void run(){
MutableInteger mi=new MutableInteger();
mi.set(num);
System.out.println("Mythread "+mi.get());
}
目前,JIT编译器可以理解MutableInteger
对象没有任何副作用,并对其对象进行优化,从而有效地将您的方法转换为:
public void run(){
System.out.println("Mythread "+num);
}
即使没有JIT编译器优化,仍然存在内存可见性问题(可以将MutableInteger
对象缓存在CPU核心一级缓存中,其他CPU核心将看不到更新的值)以及CPU指令重新排序。因此,如果没有volatile
关键字,就不能保证另一个线程会看到更改。添加volatile
关键字可以有效地将您的MutableInteger
转换为现有的AtomicInteger
(内部有一个int-volatile字段,还有更方便的方法以及setter和getter)。反对意见
我认为MutableInteger
类是线程安全的
当我声称一个类是线程安全的时,我的意思是,不同线程对其方法的重叠调用不会导致该类或该类的实例违反其API约定
MutableInteger
的API契约是什么
OP没有说明,但是任何一个合理的程序员都可以看到它的功能
我认为只要get()返回初始值0或之前设置的某个值();只要get()在同一个线程t
中返回了一个较新的值之后,再也不会返回线程t
中较旧的值,那么我认为该类的行为是合理的
保证方法调用之间的关系之前发生的任何类型的Java库类通常在其文档中详细说明这些保证。例如,从java.util.concurrent.BlockingQueue
接口的javadoc:
…将对象放入BlockingQueue
之前线程中的操作发生在另一个线程中从BlockingQueue
访问或移除该元素之后的操作之前
我没有看到任何关于MutableInteger
类的声明,因此,如果它的行为像普通int而不是volatile
int,我就不会称它为“坏的”。你有什么问题?除了MyThread.num
之外,没有其他共享数据,只有在线程尚未启动时才会设置。或者更确切地说:你想实现什么?考虑AtomicInteger。那么我应该如何从1多个线程访问同一个实例。我应该将实例公开还是将实例从一个线程传递到另一个线程?给定MutableInteger类,我只想知道如何为它编写线程类…\u我只想知道如何为它编写线程类。这是什么意思?“thread class for it”会做什么?这并没有回答“如何在线程之间共享变量”的问题,您只展示了如何获取与线程相关的错误,因此您的答案是无用的。@Krzysztofcichhoki它实际上是这样做的-特别是考虑到OP解释他实际询问的内容的评论。请注意,此测试的编写方式
public void run(){
MutableInteger mi=new MutableInteger();
mi.set(num);
System.out.println("Mythread "+mi.get());
}
public void run(){
System.out.println("Mythread "+num);
}