多线程下的计数器(线程安全)

需求:最近做一个项目需要用多线程文件落地,文件名末尾按批次号命名,file1..file2 3 4 ....

开3个固定大小的线程池,不断往里面加任务,每个线程共用一个计数器,争抢计数,从1开始递增。

package com.demo.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class MyCounter {
    private AtomicInteger batchNum = new AtomicInteger(0);

    public AtomicInteger getBatchNum() {
        return batchNum;
    }

    public void setBatchNum(AtomicInteger batchNum) {
        this.batchNum = batchNum;
    }
}
public class TestA extends Thread {
    private MyCounter myCounter;
    public TestA(MyCounter myCounter){
        this.myCounter = myCounter;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"---"+myCounter.getBatchNum().incrementAndGet());
    }
}

 

import com.demo.thread.MyCounter;

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

public class Test {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        long start = System.currentTimeMillis();
        MyCounter counter  = new MyCounter();
        while (System.currentTimeMillis() - start <10) {
            TestA t = new TestA(counter);
            executorService.execute(t);
        }
    }
}

可以看到线程池里的不同线程共享计数器 

创建一个数据库表,把打印出来的数据插入到主键字段,可以看到,正常插入,没有重复。 

 例子分析

下面例子能够使多个线程共同争抢一个计数器,不会出现两个线程之间出现重复数字。

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) {
            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) {
            System.out.println(atomicInteger.incrementAndGet());
        }
    }
}

但是,如果你要用 AtomicInteger.get()呢,因为会有很多的方法,又不想取出atomicInteger.incrementAndGet()然后一个个方法参数传进去,这样也太麻烦了。

用get()用注意,用的不好,有可能出现重复情况。上面线程TA改成如下代码:

 public void run() {
        while(System.currentTimeMillis() -start<20) {
            atomicInteger.incrementAndGet();
            System.out.println(atomicInteger.get());
        }
    }

 将打印出来的数字放入oracle主键字段,发现重复数据:

脏读的几种写法:

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次,出现一次重复。

 

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

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会出现重复,即使自增完马上取,还是会出现重复。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Java中的Thread类或者AsyncTask类来实现Android多线程计数器。 以下是使用Thread类实现的示例代码: ```java public class CounterThread extends Thread { private int count; @Override public void run() { for (int i = 0; i < 10; i++) { count++; Log.d("CounterThread", "Count: " + count); try { Thread.sleep(1000); // 暂停1秒钟 } catch (InterruptedException e) { e.printStackTrace(); } } } } ``` 在Activity中创建并启动CounterThread: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CounterThread counterThread = new CounterThread(); counterThread.start(); } } ``` 使用AsyncTask类也可以实现Android多线程计数器。以下是使用AsyncTask类实现的示例代码: ```java public class CounterTask extends AsyncTask<Void, Integer, Void> { private int count; @Override protected Void doInBackground(Void... params) { for (int i = 0; i < 10; i++) { count++; publishProgress(count); // 发布进度 try { Thread.sleep(1000); // 暂停1秒钟 } catch (InterruptedException e) { e.printStackTrace(); } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); Log.d("CounterTask", "Count: " + values[0]); } } ``` 在Activity中创建并启动CounterTask: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); CounterTask counterTask = new CounterTask(); counterTask.execute(); } } ``` 以上两种方法都可以实现Android多线程计数器,可以根据需要选择使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值