Метод можно считать потокобезопасным, если одновременные вызовы из нескольких потоков не создадут ситуации, когда любое пространство памяти пишется одним потоком и одновременно пишется/читается другим потоком в одно и то же время.
В этом примере показана функция, которая при вызове несколькими потоками может вызвать проблемы:
void doThreadNonsense(int input) {
this.myValue = input;
if (this.myValue > 9000) System.out.println("It's over 9000!");
}
Потому что здесь значение сначала записывается, а затем считывается несколькими потоками, и может случиться так, что поток A записывает 5, затем поток B записывает 9000, а затем поток A выводит предложение, потому что значение теперь > 9000, несмотря на ввод 5.
Однако с небольшим изменением эта функция становится потокобезопасной:
void doThreadNonsense(int input) {
this.myValue = input;
if (input > 9000) System.out.println("It's over 9000!");
}
Теперь значение записывается только несколькими потоками, но поскольку запись int происходит атомарно на любой 32-разрядной (или выше) машине, myValue
записывается только одним потоком или другим, но никогда обоими одновременно. Этот пример показывает, как тщательно вам нужно проверить, что делает ваш код, чтобы выяснить, является ли он потокобезопасным.
Если вы используете статический метод, он может, как указано выше, также не быть потокобезопасным:
static int myValue;
static void doThreadNonsense(int input) {
myValue = input;
if (myValue > 9000) System.out.println("It's over 9000!");
}
Однако в большинстве случаев статические функции не записывают статические переменные (это не считается чистым кодом в соответствии с шаблонами проектирования ООП), и если вы не обращаетесь ни к чему, выходящему за рамки метода, код автоматически становится потокобезопасным, потому что все используемые области памяти являются локальными только для текущего потока.
static void doThreadNonsense(int input) {
if (input> 9000) System.out.println("It's over 9000!");
}
Так что в вашей книге должно быть сказано: «Статические методы, которые следуют парадигме чистого кода, как правило, потокобезопасны».
Однако это совсем не полезно и полностью упускает из виду, почему что-то является потокобезопасным, а другое нет. Возьмем следующий пример, который на удивление (для некоторых) является потокобезопасным, несмотря на то, что он нарушает многие правила «чистой потоковой передачи»:
static final int[][] matrix = new int[N][M];
static void fillMatrixColumn(final int n) {
for (int m = 0; m < M; ++m) {
matrix[n][m] = calculateValue(n, m);
}
}
public static void main(String[] args)() {
IntStream.range(0, N)
.parallel()
.forEach(this::fillMatrixColumn);
printMatrix(matrix);
}
Никакой синхронизации, никакой блокировки, но много счастливой записи из нескольких потоков в одну и ту же структуру данных. Это по-прежнему потокобезопасно, потому что ни в коем случае два потока одновременно не пишут/читают из одной и той же области памяти.
person
TwoThe
schedule
10.08.2017
x=1;
, и у вас есть статический методstatic void increment(){x++}
, вы все равно можете столкнуться с проблемой состояния гонки. Статический просто означает, что метод вызывается в классе, а не в экземпляре. Если вы синхронизируете его, это просто означает, что вы синхронизировали его с литераломYourClass.class
, а не с экземпляромthis
. - person Pshemo   schedule 09.08.2017