マルチスレッドカウンターAtomicIntegerのダーティ読み取りを回避する方法

ダーティリードを書くいくつかの方法:

1.最初にIncrementAndGetを実行し、次に取得します

package com.demo.thread;

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

public class ThreadTest {
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) {
        int i = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        while (i < 10) {
            TA tt = new TA(atomicInteger);
            executorService.execute(tt);
            i++;
        }
    }
}

class TA implements Runnable {
    AtomicInteger atomicInteger;
    long start = System.currentTimeMillis();
    public TA(AtomicInteger atomicInteger) {
        this.atomicInteger = atomicInteger;
    }
    @Override
    public void run() {
        atomicInteger.incrementAndGet();
        try {
            //模拟程序执行耗时
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicInteger.get());

    }
}

 

2.最初にincrementAndGet、次にgetを割り当て、次にを使用します。

インターネット上の一部の人々は、それを使用する前に値を割り当てれば、あなたは汚い読書をすることはないだろうと言います。それがそうなることがわかります。

   int cnt = atomicInteger.get();
        System.out.println(cnt);

 

3.スレッドでは、while(true)はincrementAndGetを自動的にインクリメントし、ロジックに時間がかかり、その後get

package com.demo.thread;

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

public class ThreadTest {
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) {
        int i = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        while (i < 10) {
            TA tt = new TA(atomicInteger);
            executorService.execute(tt);
            i++;
        }
    }
}

class TA implements Runnable {
    AtomicInteger atomicInteger;
    long start = System.currentTimeMillis();
    public TA(AtomicInteger atomicInteger) {
        this.atomicInteger = atomicInteger;
    }
    @Override
    public void run() {
       while(System.currentTimeMillis() -start<20) {
           atomicInteger.incrementAndGet();
           try {
               //模拟程序执行耗时
               Thread.sleep(10);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
            System.out.println( atomicInteger.get());
        }
    }
}

4. while(true)をスレッド化し、incrementAndGetを最初に実行し、すぐに取得してから、ロジックに時間がかかる

package com.demo.thread;

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

public class ThreadTest {
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) {
        int i = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        while (i < 10) {
            TA tt = new TA(atomicInteger);
            executorService.execute(tt);
            i++;
        }
    }
}

class TA implements Runnable {
    AtomicInteger atomicInteger;
    long start = System.currentTimeMillis();
    public TA(AtomicInteger atomicInteger) {
        this.atomicInteger = atomicInteger;
    }
    @Override
    public void run() {
       while(System.currentTimeMillis() -start<30) {
           atomicInteger.incrementAndGet();
           int cnt = atomicInteger.get();
           try {
               //模拟程序执行耗时
               Thread.sleep(10);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
            System.out.println( cnt);
        }
    }
}

一度実行すると、ダーティリードの可能性が低くなりますが、さらに数回実行してもダーティリードが発生します。しかし、確率ははるかに小さく、約10回の実行と1回の繰り返しです。

 

ダーティリードに見えない書き込み方法を共有する:

package com.demo.thread;

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

public class ThreadTest {
    private static AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) {
        int i = 0;
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        while (i < 3) {//3改成100效果一样,还是不会出现脏读,3其实相当于Thread,没用到线程池的重复利用。
            TA tt = new TA(atomicInteger);
            executorService.execute(tt);
            i++;
        }
    }
}

class TA implements Runnable {
    AtomicInteger atomicInteger;
    long start = System.currentTimeMillis();
    public TA(AtomicInteger atomicInteger) {
        this.atomicInteger = atomicInteger;
    }
    private int cnt;//线程自己的变量
    @Override
    public void run() {
       while(System.currentTimeMillis() -start<3000) {
           cnt =  atomicInteger.incrementAndGet();
           try {
               //模拟程序执行耗时
               Thread.sleep(10);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
            System.out.println( cnt);
        }
    }
}

 総括する

1.スレッドプールにタスクを追加するときは、新しいスレッドをループに配置する必要があります。そうしないと、スレッド内のメンバー変数がプログラミングに対して安全でなくなり、ローカル変数を使用することになります。

2.タスクを追加するために新しいスレッドがループに配置され、メンバー変数をスレッドで使用できます。

3.インクリメントアンドゲット()を使用して値を取得します。getは使用しないでください。

4.インクリメントアンドゲットは重複がないことを保証でき、インクリメントが完了した直後に取得された場合でも、取得は重複します。

おすすめ

転載: blog.csdn.net/x18094/article/details/106225475
おすすめ