大家好我有一个不可变的数组实现,如下所示:
public static final class FixedArray<T> {
private final T[] array;
public final int Length;
@SafeVarargs
public FixedArray(T... args) {
array = args;
Length = args.length;
}
public T Get(int index) {
return array[index];
}
}
public static final class FixedIntArray {
private final int[] array;
public final int Length;
public FixedIntArray(int... args) {
array = args;
Length = args.length;
}
public int Get(int index) {
return array[index];
}
}
public static final class FixedLongArray {
private final long[] array;
public final int Length;
public FixedLongArray(long... args) {
array = args;
Length = args.length;
}
public long Get(int index) {
return array[index];
}
}
最初我认为它保证是线程安全的。但在阅读the discussion关于不可变数组和Java内存模型后,我相信,我无法确定。
我没有使用防御性副本,与调用代码“做正确的事”的合同(和往常一样,如果它不遵循合同,则行为未定义)。
调用方法如下所示:
public static void main(String args[]) {
int[] ints = new int[10000];
FixedIntArray fixed_ints = new FixedIntArray(ints);
SendToThreadA(fixed_ints);
SendToThreadB(fixed_ints);
SendToThreadC(fixed_ints);
SendToThreadD(fixed_ints);
//caller (which is this method) does the right thing, ints goes out of scope without anyone trying to modify it.
}
我想知道上面的代码是否保证是线程安全的?
答案 0 :(得分:3)
由于我们不知道存储引用的数组(及其值)会发生什么,我认为如果构造函数创建参数数组的副本并设置内部最终引用,则类会更安全到复制的数组。
答案 1 :(得分:1)
鉴于您可以将数组传递给varargs方法,您需要复制构造函数输入以确保它不能在类外部进行修改。完成后,只要在复制数组中分配了所有值之后不分配final
字段,就应该没问题,因为final
字段的赋值保证会发生在从另一个线程读取该字段之前。
所以构造函数看起来像:
array = Arrays.copyOf(args, args.length);
Orrrr你可以使用Guava ImmutableList并获得更多权力。
答案 2 :(得分:1)
没关系。您可以要求呼叫者将阵列“切换”给您。如有必要,来电者可以克隆一个。
内存写入通常是程序中最昂贵的东西(没有外部IO)。
并非所有人都是愚蠢的。你只需要足够的防御来保护你的目标用户群。
答案 3 :(得分:0)
我不确定检查线程安全是否有意义,因为它甚至缺少更基本的安全级别。考虑这种方法:
public static void main(final String... args)
{
final int[] arr = new int[] { 3, 3, 3 };
final FixedIntArray threeThrees = new FixedIntArray(arr);
System.out.println(threeThrees.Get(0)); // prints "3"
System.out.println(threeThrees.Get(1)); // prints "3"
System.out.println(threeThrees.Get(2)); // prints "3"
arr[0] = arr[1] = arr[2] = 4;
System.out.println(threeThrees.Get(0)); // prints "4"
System.out.println(threeThrees.Get(1)); // prints "4"
System.out.println(threeThrees.Get(2)); // prints "4"
}
问题在于,当采用int...
(或Object...
或long...
或其他任何内容)的方法时,它可以接收 一个数组由编译器隐式构造(如果你键入new FixedIntArray(3,3,3)
会发生的话),或一个由调用代码显式传入的数组(如上所述)。在后一种情况下,调用代码可以继续修改它传入的数组!