How to avoid dirty reading of multi-threaded counter AtomicInteger

Several ways of writing dirty read:

1. IncrementAndGet first, then 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() {
        atomicInteger.incrementAndGet();
        try {
            //模拟程序执行耗时
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicInteger.get());

    }
}

 

2. First incrementAndGet, then assign get, and then use.

Some people on the Internet say that if you assign a value before using it, you won’t get dirty reading. It turns out that it will.

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

 

3. In the thread, while(true) increments incrementAndGet automatically, and then logic time-consuming, then 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. Thread while(true), incrementAndGet first, get immediately, then logic time-consuming

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);
        }
    }
}

Execute once and find that the probability of dirty reads is reduced, but dirty reads still occur after several more executions. But the probability is much smaller, about 10 executions and one repetition.

 

Share a writing method that does not appear dirty read:

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);
        }
    }
}

 to sum up

1. When adding tasks to the thread pool, the new Thread must be placed in the loop. Otherwise, the member variables in the thread will be unsafe for programming and use local variables.

2. New Thread is placed in the loop to add tasks, and member variables can be used in the thread.

3. Use incrementAndGet() to get the value, don't use get.

4. incrementAndGet can guarantee that there is no duplication, and get will be duplicated, even if it is taken immediately after the increment is completed, there will still be duplication.

Guess you like

Origin blog.csdn.net/x18094/article/details/106225475