多线程计数器AtomicInteger如何避免脏读

脏读的几种写法:

1. 先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() {
        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,马上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<30) {
           atomicInteger.incrementAndGet();
           int cnt = atomicInteger.get();
           try {
               //模拟程序执行耗时
               Thread.sleep(10);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
            System.out.println( cnt);
        }
    }
}

执行一次,发现减少脏读概率,但是多执行几次还是出现脏读了。 但是概率小很多,大概执行10次,出现一次重复。

 

扫描二维码关注公众号,回复: 12627543 查看本文章

分享一个不会出现脏读的写法:

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.线程池加任务的时候,new Thread一定要放在循环里面,否则,线程里面的成员变量就编程线程不安全了,就要用局部变量取。

2.new Thread 放在循环里添加任务,线程里面就可以用成员变量。

3.用incrementAndGet()取值,别用get。

4.incrementAndGet可以保证不重复,get会出现重复,即使自增完马上取,还是会出现重复。

猜你喜欢

转载自blog.csdn.net/x18094/article/details/106225475