Mac*_*Mac 1 java multithreading immutability thread-safety
好吧,考虑Immutable下面给出的不可变类:
public final class Immutable
{
final int x;
final int y;
public Immutable(int x,int y)
{
this.x = x;
this.y = y;
}
//Setters
public int getX()
{
return this.x;
}
public int getY()
{
return this.y;
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我创建的对象Immutable的一类Sharable,其目的是要由多个线程共享:
public class Sharable
{
private static Immutable obj;
public static Immutable getImmutableObject()
{
if (obj == null) --->(1)
{
synchronized(this)
{
if(obj == null)
{
obj = new Immutable(20,30); ---> (2)
}
}
}
return obj; ---> (3)
}
}
Run Code Online (Sandbox Code Playgroud)
Thread A看到objas null并移动到synchronized块并创建对象.现在,由于Java内存模型(JMM)允许多个线程在初始化开始之后但在结束之前观察对象.因此,Thread B可以看到写入obj在写入字段之前发生Immutable.因此Thread B可以看到部分构造的Immutable可能处于无效状态并且其状态可能在以后意外地改变.
它不是Immutable非线程安全的吗?
编辑
好了,在查看了很多内容并进行了一些注释之后,我知道在构造对象之后,你可以安全地共享对线程之间的不可变对象的引用.此外,正如@Makoto所提到的,通常需要声明包含其引用volatile的字段以确保可见性.此外,正如@PeterLawrey所述,声明对不可变对象的引用final使得该字段成为thread-safe
因此,线程B可以看到在写入不可变字段之前发生的对objas的写入.因此,线程B因此可以看到部分构造的Immutable,其可能处于无效状态并且其状态可能在以后意外地改变.
在Java 1.4中,这是真的.在Java 5.0及更高版本中,最终字段在构造后是线程安全的.
你在这里描述的是两件不同的事情.首先,Immutable 是线程安全的,如果操作正在做的一个实例它.
线程安全在某种程度上是确保内存不会被另一个线程意外覆盖.就使用而言Immutable,您永远不会覆盖其实例中包含的任何数据,因此您可以确信,在并发环境中,Immutable当您构造对象时,对象将在线程操作它时相同.
你所拥有的是双重检查锁定的破坏实现.
你是对的,线程A和线程B可能会在实例设置之前对其进行践踏,从而使对象的整体不变性Immutable完全没有实际意义.
我相信解决这个问题的方法是使用volatile你的obj字段的关键字,这样Java(> 1.5)将尊重单例的预期用途,并禁止线程覆盖内容obj.
现在,读得更近了,你有一个不可变的单例,它需要两个静态数据才能存在,这似乎有点不稳定.似乎更像是适合工厂.
public class Sharable {
private Sharable() {
}
public static Immutable getImmutableInstance(int a, int b) {
return new Immutable(a, b);
}
}
Run Code Online (Sandbox Code Playgroud)
Immutable你获得的每一个实例都将是真正不可变的 - 创建一个新的Immutable对其他实例Immutable没有影响,并且使用一个实例对任何其他实例都没有影响.
| 归档时间: |
|
| 查看次数: |
1122 次 |
| 最近记录: |