Java同時(1)i++のスレッドセキュリティの問題


1.問題背景条件
  • マルチスレッド動作同一変数
  • ここではローカル変数ではありません.メンバー変数または静的変数に違いありません.
  • PS:なぜローカル変数ではないのですか?
  • は、複数のスレッドがローカル変数にアクセスする必要がある場合、匿名の内部クラス方式を使用して複数のスレッドを定義するなど、複数のスレッドをメソッドで定義する必要があります.
  • 匿名内部クラスを使用してローカル変数を参照する場合、ローカル変数はfinalによって修飾される必要があります.これは変数のライフサイクルの問題が原因です.
  • final修飾の変数は,intでもIntegerでも自己加算操作はできない.



  • 2.スレッドセキュリティの問題の原因
  • メモリ可視性
  • i++操作プロセス
  • 3.メモリの可視性
  • Javaメモリモデル
  • Aスレッド実行i++終了後、キャッシュ中の結果をメインメモリにタイムリーにリフレッシュしないと、Bスレッドがi++操作を開始し、結果がエラーになります.
  • ソリューション:volatileキーワード修飾を使用します.

  • 4.i++の操作手順
    4.1. i++またはJavaコードの実際の操作手順をどのように知るか?
    Javap逆コンパイルコードにより、バイトコードコマンドを分析します(アセンブリのような感じがします)
    int i = 0;
    i = i++;
    //        i = 0 ?
    //           
    /*
           0: iconst_0  //   0    
           1: istore_1  //   int      1      i=0
           2: iload_1       //  1 int           tp = i
           3: iinc          1, 1    //  1      1(          )i = i+1
           6: istore_1  //          1       i = tp
           7: return
    */

    4.2. i++のバイトコード
  • メンバー変数の自己増加操作
  • /*
           0: aload_0       //                 , this
           1: dup   //      ,        
           2: getfield      #2  //       ,           tp = i
           5: iconst_1  //   1     tp2 = 1
           6: iadd      //      int    ,         tp3 = tp+tp2
           7: putfield      #2  //      i=tp3
          10: return
    */
  • 静的変数の自己増加動作
  • /*
           0: getstatic     #5  //     ,         tp = i
           3: iconst_1  //   1      tp2 = 1
           4: iadd      //      int    ,         tp3 = tp+tp2
           5: putstatic     #5  //      i=tp3
           8: return
    */
  • まとめ
  • //i++         
    tp = i //1
    tp2 = tp+1 //2
    i = tp2 //3

    4.3. スレッドセキュリティの問題
    4.3.1.原理疑似コード
    //          i,         i++  
    static int i = 0;
    
    //  A        
    int tp1 = i;    //1
    int tp2 = tp1+1; //2
    i = tp2;    //3
    
    //  B        
    int tp3 = i;    //4
    int tp4 = tp3+1;    //5
    i = tp4;    //6
    
    //          1->2->3->4->5->6,         
    //         1->4->2->3->5->6,         

    4.3.2.実行可能なテストコード
    //     0,           
    public class IncTest {
        private volatile static int i = 0;
    
        public static void main(String[] args) throws InterruptedException {
            Thread a = new Thread() {
                @Override
                public void run() {
                    for( int j = 0 ; j < 1000000 ; j++ ) i++;
                }
            };
            a.start();
            Thread b = new Thread() {
                @Override
                public void run() {
                    for( int j = 0 ; j < 1000000 ; j++ ) i--;
                }
            };
            b.start();
            a.join();
            b.join();
            System.out.println(i);
        }
    }