Varargs, неизменный массив и безопасность потоков - PullRequest
3 голосов
/ 19 ноября 2011

Привет всем, у меня есть реализация неизменяемого массива, которая выглядит так:

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];
    }
}

Изначально я думал, что она гарантированно поточно-ориентирована.Но после прочтения обсуждения , касающегося неизменяемых массивов и модели памяти 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.
}

Мне было интересно, гарантированно ли приведенный выше код является поточно-ориентированным?

Ответы [ 4 ]

3 голосов
/ 19 ноября 2011

Поскольку мы не знаем, что происходит с массивом (и его значениями), на который вы сохраняете ссылку, я думаю, что ваши классы были бы намного безопаснее, если бы конструкторы создали копию массива аргументов и установили внутреннюю окончательную ссылкув скопированный массив.

1 голос
/ 20 ноября 2011

Все нормально.Вы можете потребовать, чтобы вызывающая сторона "передала" массив вам.Вызывающий может при необходимости клонировать его.

Запись в память, как правило, самая дорогая вещь в программе (без внешнего ввода-вывода).

Не все глупы.Вам нужно быть достаточно защищенным, чтобы защитить целевую базу пользователей.

1 голос
/ 19 ноября 2011

Учитывая, что вы можете передать массив в метод varargs, вам нужно будет сделать копию входных данных конструктора, чтобы гарантировать, что он не может быть изменен вне класса.Сделав это, до тех пор, пока вы не назначите поле final до тех пор, пока все значения не будут назначены в массиве копирования, у вас все будет хорошо, потому что присвоение полю final гарантированно произойдет до любого чтенияэто поле из другого потока.

Таким образом, конструктор будет выглядеть так:

array = Arrays.copyOf(args, args.length);

Orrrr, вы можете просто использовать Guava ImmutableList и получитьнамного больше силы.

0 голосов
/ 19 ноября 2011

Я не уверен, что имеет смысл проверять его на предмет безопасности потоков, потому что в нем отсутствует даже более базовый уровень безопасности. Рассмотрим этот метод:

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)), или массив, который явно передается вызывающим кодом (как я делал выше). В последнем случае вызывающий код может продолжать изменять массив, в котором он был передан!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...