Я знаю, что i++ не работает в потоковом режиме. Я также понимаю, почему i++ работает быстрее, чем я = я + 1. Является ли я = я + 1 отличным от i++ с точки зрения безопасности потоков? Любое объяснение уровня байткода было бы действительно полезно.
Ни i += 1
ни i++
не являются потокобезопасными. То же самое касается ++i
. Вот простой тест, который вы можете проверить, чтобы доказать это:
public class Test {
static volatile int x, y;
static class IncThread extends Thread {
public void run() {
for (int i=0; i<50000; i++) x++;
for (int i=0; i<50000; i++) y = y+1;
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new IncThread();
Thread t2 = new IncThread();
t1.start();
t2.start();
t1.join();
t2.join();
System.out.printf("x = %d, y = %d%n", x, y);
}
}
Вот что я получаю для вывода:
x = 99897, y = 81556
Очевидно, что некоторые из записей были потеряны. Там хороший маленький пост в блоге, ++ не считается атомарным, что объясняет это. Этот пост также указывает, что ответ @August вводит в заблуждение. Этот байт-код (iinc
) генерируется только для увеличения локальных переменных, которые не интересны с точки зрения безопасности потоков. (В блоге также рассказывается о разных байткодах, используемых для приращений.)
Нет никакой разницы между i++
и i += 1
, байт-код:
Приращение (источник):
public static void main(String[] args) {
int i = 0;
i++;
}
Приращение (байт-код):
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: return
Добавление соединения (источник):
public static void main(String[] args) {
int i = 0;
i += 1;
}
Добавление соединения (байт-код):
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc 1, 1
5: return
Байт-код, используемый для приращения полей, также одинаков, хотя он не использует инструкцию iinc
(поскольку для этого нужен локальный индекс переменной):
int x;
void inc() { x++; }
void assign() { x += 1; }
void inc();
Code:
0: aload_0
1: dup
2: getfield #2 // Field x:I
5: iconst_1
6: iadd
7: putfield #2 // Field x:I
10: return
void assign();
Code:
0: aload_0
1: dup
2: getfield #2 // Field x:I
5: iconst_1
6: iadd
7: putfield #2 // Field x:I
10: return
javap -c Foo.class
(или мой довольно дурацкий сайт )
i=i+1
использует двоичный оператор (+), который загружает значение я и добавляет его к нему, а затем сохраняет результат обратно в i. Напротив, i++
использует унарный (++) оператор, который просто увеличивает значение с помощью одной команды сборки, поэтому теоретически это может быть более эффективным. Однако сегодня оптимизация компиляторов i=i+1
и i++
приведет к тому же оптимизированному коду.
В этих терминах нет различий между i++ и я = я + 1.