多线程系统的Java不可变对象。我做错了什么?

多线程系统的Java不可变对象。我做错了什么?,java,multithreading,concurrency,immutability,Java,Multithreading,Concurrency,Immutability,我是个不可变对象的傻瓜。我的想法是,它们在多线程环境中很有用,而且从定义上讲是线程安全的 但是下面的代码似乎正好相反 public class ImmutableTest { volatile ImmutableObject obj; public static void main(String[] args) throws InterruptedException { new ImmutableTest().execute(); } pri

我是个不可变对象的傻瓜。我的想法是,它们在多线程环境中很有用,而且从定义上讲是线程安全的

但是下面的代码似乎正好相反

public class ImmutableTest {

    volatile ImmutableObject obj;

    public static void main(String[] args) throws InterruptedException {
        new ImmutableTest().execute();
    }

    private void execute() throws InterruptedException
    {
        obj = new ImmutableObject(0);
        ExecutorService exec = Executors.newFixedThreadPool(50);    
        for(int i = 0; i < 50; i++){
            exec.execute(new ImmutableRunnable(this));
        }
        Thread.sleep(5000);
        exec.shutdown();
        obj.print();
    }
}


public class ImmutableRunnable implements Runnable {
    ImmutableTest test;

    ImmutableRunnable(ImmutableTest immutableTest) {
        this.test = immutableTest;
    }

    public void run() {
        this.test.obj = new ImmutableObject(this.test.obj.getValue());
}

}

public final class ImmutableObject {

    private int n;

    public ImmutableObject(int newValue) {
        this.n = newValue;
        if(Math.random() > .5){
            try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
                e.printStackTrace();
        }
        }
    }

    public void print() {
        System.out.println(n);
    }

    public int getValue() {
        return n;
    }
}
公共类不可变测试{
易失性不可变对象obj;
公共静态void main(字符串[]args)引发InterruptedException{
新建ImmutableTest().execute();
}
private void execute()引发InterruptedException
{
obj=新的不可变对象(0);
ExecutorService exec=Executors.newFixedThreadPool(50);
对于(int i=0;i<50;i++){
exec.execute(新的Immutablerunable(this));
}
睡眠(5000);
exec.shutdown();
obj.print();
}
}
公共类ImmutableRunnable实现Runnable{
免疫试验;
Immutablerunable(ImmutableTest ImmutableTest){
this.test=不可变测试;
}
公开募捐{
this.test.obj=新的ImmutableObject(this.test.obj.getValue());
}
}
公共最终类ImmutableObject{
私人int n;
公共不可变对象(int newValue){
这个.n=新值;
if(Math.random()>.5){
试一试{
睡眠(100);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
}
公开作废印刷品(){
系统输出println(n);
}
public int getValue(){
返回n;
}
}
在线程安全的环境中,我希望结果是50。但事实并非如此。如果从ImmutableObject构造函数中取消随机睡眠,则得到50

那么结论是什么?不可变对象是线程安全的“如果构造函数足够快”?或者(更可能)我误解了什么

对不起,我没有正确地解释我的疑问。这不是一个同步问题,我学习了如何同步方法和使用锁。这个问题涉及不可变对象及其与多线程的关系。我到处都读到一个不可变的对象是线程安全的,因此它可以在线程之间毫无畏惧地共享。但我觉得这根本不是真的!!! 所以你能不能给我举个例子,一个不可变的对象在不同的线程之间共享,它的使用不需要同步?
谢谢各位。

你们误解了什么

不可变对象允许您安全地共享对象并读取任何方法/字段,而无需使用锁

在这种情况下,您需要依赖可变的
obj
引用以某种方式进行同步-事实并非如此


不变性只是一个不能修改的对象。修改状态后,需要执行一些同步,以确保所需的实际发生。例如,整个“通过读取obj设置obj”调用需要是独占的,并且以串行方式发生。

您误解了某些内容

不可变对象允许您安全地共享对象并读取任何方法/字段,而无需使用锁

在这种情况下,您需要依赖可变的
obj
引用以某种方式进行同步-事实并非如此

不变性只是一个不能修改的对象。修改状态后,需要执行一些同步,以确保所需的实际发生。例如,整个“通过读取obj设置obj”调用需要是独占的,并且以串行方式发生。

Immutablerunable.run()不是线程安全的:

  • 它读取obj中的值
  • 它创建一个新的不可变对象
  • 它更新了obj
显然,如果这些步骤当时正在执行,那么您可能会遇到问题。问题不在于不可变对象,而是在没有正确锁定的情况下对obj进行了变异

解决此问题的最简单方法是在ImmutableTest上同步。

ImmutableRunnable.run()不是线程安全的:

  • 它读取obj中的值
  • 它创建一个新的不可变对象
  • 它更新了obj
显然,如果这些步骤当时正在执行,那么您可能会遇到问题。问题不在于不可变对象,而是在没有正确锁定的情况下对obj进行了变异


解决这个问题的最简单方法是在ImmutableTest上同步。

正如Yann Ramin所说,不可变对象允许您安全地共享和使用对象,而无需使用锁


但是,您在这里测试的是写入类
Immutabletest
obj
字段的线程安全性<代码>不可变测试不是不可变的,因此不是线程安全的。

正如Yann Ramin所说,不可变对象允许您安全地共享和使用对象,而无需使用锁

但是,您在这里测试的是写入类
Immutabletest
obj
字段的线程安全性<代码>不可变测试不是不可变的,因此不是线程安全的

  • 您提供的代码将始终打印0。这仅仅是因为您正在复制值并且从不更改它,所以它只能是起始值0
  • ImmutableObject不是不可变的。。。一个不可变的对象只有最终字段,这就保证了状态的不完整发布。但是,这对您在这里执行的代码没有任何影响
  • 如果我通过将ImmutableObject或ImmutableRunnable增量设置为值,将代码修复为我认为您想要的,那么您将遇到上述问题,也就是说volatile保证可见性/顺序,但不保证原子性。要使代码在没有同步的情况下正常工作,可以使用AtomicReference.compareAndSet来确保要替换的Immutable对象是用作下一个对象基础的对象。 例如:
  • c
    
        AtomicReference obj = new AtomicReference();// was volatile obj
    ... change obj access in execute to match
    ... replace run method with following:
        ImmutableObject curr;
        do{
           curr = this.test.obj.get();
        }while(!this.test.obj.compareAndSet(curr, new ImmutableObject(curr.getValue()+1)));