Javaのマルチスレッド - 同期変数との同時アクセス

あまりにも非スレッドセーフの場合には、同じオブジェクトに複数のスレッドによる同時アクセスは、結果は読みすぎて汚れている結果として、インスタンス変数だった、つまり、データをとっているのは実際に素敵な変化です。

変数は、内部メソッドは、プライベートた「非スレッドセーフ、」問題が存在しない場合、また、非スレッド安全性の問題は、「インスタンス変数」に存在します。

1同期

1.1同期方法

public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add1);
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    private Add a;
    public ThreadAA(Add add){
        this.a = add;
    }
    @Override
    public void run(){
        a.add("a");
    }
}

class ThreadBB extends Thread{
    private Add b;
    public ThreadBB(Add add){
        this.b = add;
    }
    @Override
    public void run(){
        b.add("b");
    }
}

class Add{
    private int num = 0;
    //同步方法
    synchronized public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

印刷結果

add a end
add b end
b name 200
a name 100
复制代码

ので、プリント注文の結果から、同期が、クロスされていないロックが同期オブジェクトロックのキーワードですメソッドのロックを保持しているスレッドがオブジェクトに属している複数のスレッドが同じオブジェクトにアクセスするのであれば方法上記の例では、synchronizedキーワードを持つ最初のスレッドの実行が、その後、他のスレッドが唯一の状態で待機することができます。

オブジェクトロックのためにロックを保持する同期方法を確認してください

//将上面的ThreadTest类中的main方法进行修改
public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
//        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add);
        threadBB.start();
    }
}
复制代码

業績

add a end
a name 100
add b end
b name 200
复制代码

操作のさらに結果を表示するには、この時間は、印刷の順番です。

1.2同期シンクブロック

欠点は、スレッドがタスクを実行するには長い時間同期メソッドを呼び出すなど、ある同期方法上述したように、いくつかのケースで宣言されたsynchronizedメソッドを使用して、他のスレッドは長時間待たなければなりません。このような場合には、我々は、コードセクションが同期的に実行する必要がありますラップする同期同期ブロックを使用して、解決するための同期ブロックの同期コードを使用することができます。

public class ThreadFunction {
    public static void main(String[] args) {
        ObjFunction objFunction = new ObjFunction();
        FunA funA = new FunA(objFunction);
        funA.setName("a");
        funA.start();
        FunB funB = new FunB(objFunction);
        funB.setName("b");
        funB.start();

    }
}

class FunB extends Thread{
    private ObjFunction objFunction;
    public FunB(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class FunA extends Thread{
    private ObjFunction objFunction;
    public FunA(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class ObjFunction{
    public void objMethod(){
        try{
            System.out.println(Thread.currentThread().getName() + " start");
            synchronized (this) {
                System.out.println("start time = " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end time = "+ System.currentTimeMillis());
            }
            System.out.println(Thread.currentThread().getName() + " end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

業績

a start
b start
start time = 1559033466082
end time = 1559033468083
a end
start time = 1559033468083
end time = 1559033470084
b end
复制代码

図から分かるように、追加のコード同期ブロックは、同期コードブロックが同期して実行され、非同期です。そして、同期(本)ロックされているオブジェクトが現在のオブジェクトです。

加えて、thisロックオブジェクトとして、Javaはまた、機能を同期させるためのロックとして任意のオブジェクトをサポートしていますが、同期モニターと同じオブジェクトであるか、または他の結果は、非同期呼び出しで実行しなければならないことに注意すること。

1.3同期静的な同期メソッド

synchronizedキーワード静的にも、静的メソッドに適用することができるので、現在、対応するクラス*ロックオブジェクトとしての.javaクラスファイル。

ロックオブジェクトを保持する静的同期方法=同期(クラス)

public class ThreadTest {
    public static void main(String[] args) {
        ThreadAA threadAA = new ThreadAA();
        threadAA.start();
        ThreadBB threadBB = new ThreadBB();
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    @Override
    public void run(){
        Add.add("a");
    }
}

class ThreadBB extends Thread{
    @Override
    public void run(){
        Add.add("b");
    }
}

class Add{
    private static int num = 0;
    //同步方法
    synchronized static public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

業績

add a end
a name 100
add b end
b name 200
复制代码

1.4同期类

クラスのキーワード同期修正を使用して、クラスメソッドは、コードが表示されないようなすべての同期方法、です。

1.5同期ロックリエントラント

糸ロックは、オブジェクトは、このオブジェクトが再びロックすることが可能であり、再度オブジェクトロック要求を与えるとき、それは、使用時に同期リエントラントロック機能、すなわち、同期しています。内部同期メソッド/コードブロックは、他の同期/ブロックの現在のメソッドを呼び出すようにする場合つまり、あなたは常に入手することができます。

public class ThreadAgain {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Service().service1();
            }
        }).start();
    }
}

class Service{
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized private void service2() {
        System.out.println("service2");
        service3();
    }

    synchronized private void service3() {
        System.out.println("service3");
    }

}
复制代码

業績

service1
service2
service3
复制代码

2揮発性

volatileキーワードの主な役割は、複数のスレッド間で変数に表示されます。

むしろ、プライベートデータスタックスレッドから取得した変数の値よりも、一般的なスタックから変数の値を得ることが必須です。複数のスレッドでは、スタックとプログラムカウンタは、プライベートで、スタックおよびグローバル変数が働いています。

コードを見てください

public class MyVolatile {
    public static void main(String[] args) {
        try {
            RunThread runThread = new RunThread();
            runThread.start();
            Thread.sleep(2000);
            runThread.setRun(false);
            System.out.println("为runThread复制false");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

class RunThread extends Thread{
    private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

あなたは、スレッドが終了していない、コンソールから見ることができます。問題は、プライベート・スタックの値と作業の積み重ねが、あなたは揮発性のキーワードを使用することができ、この問題を解決しようとすることで生じた同期の値を有することです。

クラスのコードを変更しRunThread

class RunThread extends Thread{
   volatile private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

再び実行し、通常の終了を通します。

volatileキーワードは、複数のスレッド間でインスタンス変数で見ることができますが、アトミックサポートしていない揮発性の致命的な欠陥がありますが。

認証揮発性の原子性をサポートしていません。

public class IsAtomic {
    public static void main(String[] args) {
        MyAtomicRun[] myAtomicRuns = new MyAtomicRun[100];
        for (int i = 0;i<100;i++){
            myAtomicRuns[i] = new MyAtomicRun();
        }
        for (int i = 0;i<100;i++){
            myAtomicRuns[i].start();
        }
    }
}

class MyAtomicRun extends Thread{
    volatile public static int count;
    private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

プリント出力

//篇幅较长,没有全部粘贴
count: 5000
count: 4900
count: 4800
count: 4700
count: 4600
count: 4500
count: 4400
count: 4400
复制代码

ビューの出力からの結果は、何も出力10000と私たちの理想的な状態ではありません。

コードの改良

class MyAtomicRun extends Thread{
    volatile public static int count;
    //需要使用同步静态方法,这样是以class为锁,才能达到同步效果
    synchronized private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

プリント出力

count: 9300
count: 9400
count: 9500
count: 9600
count: 9700
count: 9800
count: 9900
count: 10000
复制代码

今回は、出力は正しい結果です。

マルチスレッド共有変数を読み込む際に、インスタンス変数を知覚することができ、複数のスレッドでvolatileキーワードの主な用途が変更された場合、あなたはそれがある、最新の値が使用され得ることができ、あなたは、最新の値を取得することができます。

揮発性と同期の2.1比較

  1. 軽量な実装の揮発性のスレッド同期は、そのパフォーマンスは確かに同期され、揮発性変数のみ変更、および同期メソッドを、コードブロックを変更することができることができるよりも優れています。
  2. 揮発性のブロッキングは、マルチスレッドアクセスを発生し、そこにはブロックされます同期されません。
  3. 揮発性データの可視性を確保するが、原子性を保証しないように、両方の同期とアトミック保証、またはデータがプライベートメモリと共通メモリの同期を同期しているので、間接的に、視認性を確保。
  4. 溶液は、変数の複数のスレッドの可視性の間で揮発性であり、同期ソリューションは、複数のスレッドの同期の間でリソースにアクセスすることです。

2.2変数のワーキングメモリ

上記揮発性キーワード修飾変数のような動作++オペレータが実際にアトミック操作ではないように、それは、スレッドセーフではありません。

私は手順を++します:

  1. 私はメモリから値をとります
  2. 私は、コンピューティング
  3. 私は、メモリに書き込まれます

別のスレッドの計算の第二工程における時間はまた、iの値を変更した場合、時間が古いデータであろう。

17138799-194122859a667229.png
image.png

<figcaption> </ figcaption>

  • 読んでステージをロードします。現在のスレッドにメインメモリコピー変数からメモリを作業。
  • 使用してステージを割り当てる:実行コード、共有変数の値を変更します。
  • ストアと書き込みフェーズ:ワーキングメモリとメインデータの変数値に対応するリフレッシュメモリ。

マルチスレッド環境で、使用して、オーバーロードされたように、メモリのワーカースレッド複数発生しているが、メインメモリ変数の値が変更された場合、この動作は、ステージを読んだ後、すなわち、アトミックではない割り当てますので、同じ、非スレッド安全性の問題ではないプライベートメモリとパブリックメモリ変数の値は、同期算出し、結果を期待されない原因、対応する変化を生じません。

volatileキーワードに修正される変数については、JVMのみ保証値では、ワーキングメモリにメインメモリからロードされた最新のものです。

ます。https://www.jianshu.com/p/bd8877ad99d8で再現

おすすめ

転載: blog.csdn.net/weixin_33813128/article/details/91303300