Разница между i ++ и i = i + 1 с точки зрения атомарности

1

Я знаю, что i++ не работает в потоковом режиме. Я также понимаю, почему i++ работает быстрее, чем я = я + 1. Является ли я = я + 1 отличным от i++ с точки зрения безопасности потоков? Любое объяснение уровня байткода было бы действительно полезно.

  • 7
    Они компилируются с одинаковым байт-кодом , поэтому они должны иметь одинаковую производительность.
  • 1
    @August2 Август2 - Отлично. Вы должны опубликовать это как ответ.
Теги:
thread-safety
atomicity

4 ответа

9
Лучший ответ

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

  • 0
    Если у вас есть проблема с ответом Августа, вы должны поместить комментарий под этим ответом и, возможно, понизить голос. Кто-то может использовать этот ответ, и даже не видеть ваш.
6

Нет никакой разницы между 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    
  • 0
    Как вы читаете байт-код из скомпилированного источника?
  • 2
    @DavidJones Вы можете использовать javap -c Foo.class (или мой довольно дурацкий сайт )
Показать ещё 2 комментария
1

i=i+1 использует двоичный оператор (+), который загружает значение я и добавляет его к нему, а затем сохраняет результат обратно в i. Напротив, i++ использует унарный (++) оператор, который просто увеличивает значение с помощью одной команды сборки, поэтому теоретически это может быть более эффективным. Однако сегодня оптимизация компиляторов i=i+1 и i++ приведет к тому же оптимизированному коду.

-1

В этих терминах нет различий между i++ и я = я + 1.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню