这篇文章将为大家详细讲解有关如何实现Java线程安全的计数器,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
前几天工作中一段业务代码需要一个变量每天从1开始递增。为此自己简单的封装了一个线程安全的计数器,可以让一个变量每天从1开始递增。当然了,如果项目在运行中发生重启,即便日期还是当天,还是会从1开始重新计数。所以把计数器的值存储在数据库中会更靠谱,不过这不影响这段代码的价值,现在贴出来,供有需要的人参考。
package com.hikvision.cms.rvs.common.util; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; /** * Created by lihong10 on 2017/8/9. * 一个循环计数器,每天从1开始计数,隔天重置为1。 * 可以创建一个该类的全局对象,然后每次使用时候调用其get方法即可,可以保证线程安全性 */ public class CircularCounter { private static final AtomicReferenceFieldUpdater<CircularCounter, AtomicInteger> valueUpdater = AtomicReferenceFieldUpdater.newUpdater(CircularCounter.class, AtomicInteger.class, "value"); //保证内存可见性 private volatile String key; //保证内存可见性 private volatile AtomicInteger value; private static final String DATE_PATTERN = "yyyy-MM-dd"; public CircularCounter() { /** * 这里将key设置为getCurrentDateString() + "sssssssssss" 是为了测试addAndGet()方法中日期发生变化的情况 * 正常使用应该将key初始化为getCurrentDateString() */ this.key = getCurrentDateString() + "sssssssssss"; this.value = new AtomicInteger(0); } /** * 获取计数器加1以后的值 * * @return */ public Integer addAndGet() { AtomicInteger oldValue = value; AtomicInteger newInteger = new AtomicInteger(0); int newVal = -1; String newDateStr = getCurrentDateString(); //日期一致,计数器加1后返回 if (isDateEquals(newDateStr)) { newVal = add(1); return newVal; } //日期不一致,保证有一个线程重置技术器 reSet(oldValue, newInteger, newDateStr); this.key = newDateStr; //重置后加1返回 newVal = add(1); return newVal; } /** * 获取计数器的当前值 * @return */ public Integer get() { return value.get(); } /** * 判断当前日期与老的日期(也即key成员变量记录的值)是否一致 * * @return */ private boolean isDateEquals(String newDateStr) { String oldDateStr = key; if (!isBlank(oldDateStr) && oldDateStr.equals(newDateStr)) { return true; } return false; } /** * 如果日期发生变化,重置计数器,也即将key设置为当前日期,并将value重置为0,重置后才能接着累加, */ private void reSet(AtomicInteger oldValue, AtomicInteger newValue, String newDateStr) { if(valueUpdater.compareAndSet(this, oldValue, newValue)) { System.out.println("线程" + Thread.currentThread().getName() + "发现日期发生变化"); } } /** * 获取当前日期字符串 * * @return */ private String getCurrentDateString() { Date date = new Date(); String newDateStr = new SimpleDateFormat(DATE_PATTERN).format(date); return newDateStr; } /** * 计数器的值加1。采用CAS保证线程安全性 * * @param increment */ private int add(int increment) { return value.addAndGet(increment); } public static boolean isBlank(CharSequence cs) { int strLen; if(cs != null && (strLen = cs.length()) != 0) { for(int i = 0; i < strLen; ++i) { if(!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } else { return true; } } public static void test() { CircularCounter c = new CircularCounter(); AtomicInteger count = new AtomicInteger(0); List<Thread> li = new ArrayList<Thread>(); int size = 10; CountDownLatch latch2 = new CountDownLatch(1); CountDownLatch latch3 = new CountDownLatch(size); for (int i = 0; i < size; i++) { Thread t = new Thread(new CounterRunner(c, latch2, latch3, count), "thread-" + i); li.add(t); t.start(); } System.out.println("start"); latch2.countDown(); try { latch3.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(count.get()); System.out.println(c.get()); if(count.get() == c.get()) { System.out.println("该计数器是线程安全的!!!"); } } public static void main(String... args) { for(int i = 0; i < 15; i++) { test(); } } } /** * 测试使用的Runnable对象 */ class CounterRunner implements Runnable { private CircularCounter counter; private CountDownLatch latch2; private CountDownLatch latch3; private AtomicInteger count; public CounterRunner(CircularCounter counter, CountDownLatch latch2, CountDownLatch latch3, AtomicInteger count) { this.latch2 = latch2; this.latch3 = latch3; this.counter = counter; this.count = count; } @Override public void run() { try { latch2.await(); System.out.println("****************"); for (int i = 0; i < 20; i++) { counter.addAndGet(); count.addAndGet(1); } latch3.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }
关于“如何实现Java线程安全的计数器”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。