JAva同時メモ


1.java同時実行中の同期、原子操作とvolatileキーワードの使い方
同期ルールが使用されている場合:変数を書いている場合は、次に別のスレッドに読み込まれるか、前回他のスレッドに書かれた変数を読み込んでいる場合は、同期を使用して、同じモニタロックを使用して、すべてのアクセス方法が同期されていることを保証する必要があります.
原子間操作とは、スレッドスケジューリングメカニズムによって中断されない操作であり、操作が開始されると、可能なコンテキスト切替(他のスレッドに切り替える)の前に実行されるに違いない.プログラミングでは、原子性に依存することは困難で危険であり、原子性は一般的にlongおよびdouble以外のすべての基本タイプの上の簡単な操作、例えば基本タイプ変数の読み取りまたは書き込みに適用することができる.
jvmは64ビット(longとdouble)変数の読み出し書き込みを2つの分離した32ビット操作に分けることができるため、この2つの変数の読み出し書き込みは原子的ではなく、volatileキーワードを使用してlongまたはdouble変数を定義することによって解決することができる.さらにvolatileは、マルチプロセッサシステムに適用される可視性を保証します.マルチプロセッサシステムでは、変更はローカルのプロセッサのキャッシュに一時的に格納されるだけで、プライマリ・メモリに書き込まれません.このように、ドメインをvolatileと宣言すると、このドメインを書き込み操作すると、プロセッサは書き込み操作の結果をプライマリ・メモリにブラシします.(リード操作は、ホストメモリによるもの)あるドメインの値他のタスクが最新の値を読み取る必要がない場合、ドメインの原子操作がプライマリ・メモリにリフレッシュされる必要がないため、キーワードを使用する必要はありません.複数のプロセッサが同じ時刻に読み取った値が同じであることを保証する必要がある場合、この値はvolatileでなければなりません.そうでない場合は、同期を使用してアクセスする必要があります.同期は、相プライマリ・ストレージでリフレッシュすることもあります.したがって、ドメインのすべてのアクセス・メソッドがsynchronizedを使用してロック保護されている場合は、volatileに設定する必要はありません.
注意すべきは、c++とは異なりjavaにおける自己増加動作は原子性ではなく、例えば以下のコードである.
i++;

従って、synchronizedキーワード保護を使用するのが一般的であるが、パフォーマンスチューニングなどの効率性が必要な場合には、AtomicInteger、AtomicLong、AtomicReferenceなどの特殊な原子変数クラスを使用することができる.例は以下の通りである.
package concurrency;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by xudong on 2018/7/7.
 */
public class AtomicInterTest implements Runnable {
    private AtomicInteger i = new AtomicInteger(0);
    public int getValue() {
        return  i.get();
    }
    private void evenIncrement(){
        i.addAndGet(2); //  ++i; ++i
    }
    @Override
    public void run() {
        while (true){
            evenIncrement();
        }
    }

    public static void main(String[] args) {
        //Timer       ,       ,    
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                System.err.println("Aborting");
                System.exit(0);
            }
        }, 5000 );  //     5     
        ExecutorService exec = Executors.newCachedThreadPool();
        AtomicInterTest ai = new AtomicInterTest();
        exec.execute(ai);
//            ,     
        while(true){
            int val = ai.getValue();
            if(val % 2 != 0){
                System.out.println(val);
                exec.shutdown();
                System.exit(0);
            }
        }

    }
}

AtomicIntegerの自己増加関数を用いることで原子性が保証されるため,このプログラムは失敗しない
2.finalキーワードの異なる役割ドメインでの使用
finalという修飾子は変えられないことを表していますが、異なる場所では異なる意味を表しています.
public final class Foo{}

Fooというクラスをメソッドに継承できないことを表しています
この方法が書き換えられないことを意味します
public class Foo{
public final void get(){}
}
public class Fool extends Foo{
//         get()   
}

フィールド上
このフィールドは再割り当てできませんが、注意してください.
public class Foo{
public final int i;//  i    
public Foo(int i){
this.i = i;//                ,          
}
}

public class Foo{
public final int i = 0;//  i   
public Foo(int i){
//           ,         ,        
}
}

ただし、フィールドの前では、値を再割り当てできないだけで、それ自体の値を変更できないわけではありません.たとえば、集合クラスList、Map、Setなど、add、removeなどのメソッドを実行できますが、オブジェクトに値を再割り当てすることはできません.
final List list = new ArrayList(); //   list     (   )       。