Java在高并发场景下实现点赞计数器
更新时间:2023年06月28日 16:01:57 作者:SSPo
点赞计数器的本质就是对某个变量在高并发情况下的修改,这篇文章主要为大家介绍了Java实现点赞计数器的示例代码,感兴趣的小伙伴可以了解一下
点赞计数器的本质就是对某个变量在高并发情况下的修改,volatile关键字只能保证变量的可见性和有序性,不能保证其原子性
使用方案有如下选择:
1. Synchronized 关键字
package concurrent; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; class ClickResource{ int number = 0; public synchronized void clickBySynchronized(){number++;} } // 100个线程, 每个线程点赞 100万 次, 求准确率和效率 public class AccumulatorCompareDemo { public static final int _1w = 10000; public static final int threadNumber = 100; public static void main(String[] args) throws InterruptedException { ClickResource clickResource = new ClickResource(); long startTime = System.currentTimeMillis(); CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { new Thread(()->{ try { for (int j = 0; j < 100*_1w; j++) { clickResource.clickBySynchronized(); } }finally { countDownLatch1.countDown(); } },String.valueOf(i)).start(); } countDownLatch1.await(); long endTime = System.currentTimeMillis(); System.out.println("所用时间="+(endTime-startTime)+"ms"+" 最终点赞次数="+clickResource.number); } } // synchronized 悲观锁 // 所用时间=2258ms 最终点赞次数=100000000
2. 使用 AtomicLong 原子类
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; class ClickResource{ int number = 0; AtomicLong atomicLong = new AtomicLong(0); public void clickByAtomicLong(){atomicLong.getAndIncrement();} } // 100个线程, 每个线程点赞 100万 次, 求准确率和效率 public class AccumulatorCompareDemo { public static final int _1w = 10000; public static final int threadNumber = 100; public static void main(String[] args) throws InterruptedException { ClickResource clickResource = new ClickResource(); long startTime = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { new Thread(()->{ try { for (int j = 0; j < 100*_1w; j++) { clickResource.clickByAtomicLong(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("所用时间="+(endTime-startTime)+"ms"+" 最终点赞次数="+clickResource.atomicLong); } } // CAS - 轻量级锁 - 调用 Unsafe 类的 cas 方法 - 底层操作系统原子指令 // 所用时间=1177ms 最终点赞次数=100000000
3.使用 LongAdder 类
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; class ClickResource{ int number = 0; LongAdder longAdder = new LongAdder(); public void clickByLongAdder(){longAdder.increment();} } // 100个线程, 每个线程点赞 100万 次, 求准确率和效率 public class AccumulatorCompareDemo { public static final int _1w = 10000; public static final int threadNumber = 100; public static void main(String[] args) throws InterruptedException { ClickResource clickResource = new ClickResource(); long startTime = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadNumber); CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber); CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber); CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { new Thread(()->{ try { for (int j = 0; j < 100*_1w; j++) { clickResource.clickByLongAdder(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("所用时间="+(endTime-startTime)+"ms"+" 最终点赞次数="+clickResource.longAdder); } } //所用时间=141ms 最终点赞次数=100000000
4. 使用 LongAccumulator 类
import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; class ClickResource{ int number = 0; LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0); public void clickByLongAccumulator(){longAccumulator.accumulate(1);} } // 100个线程, 每个线程点赞 100万 次, 求准确率和效率 public class AccumulatorCompareDemo { public static final int _1w = 10000; public static final int threadNumber = 100; public static void main(String[] args) throws InterruptedException { ClickResource clickResource = new ClickResource(); long startTime = System.currentTimeMillis(); CountDownLatch countDownLatch = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { new Thread(()->{ try { for (int j = 0; j < 100*_1w; j++) { clickResource.clickByLongAccumulator(); } }finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); } countDownLatch.await(); long endTime = System.currentTimeMillis(); System.out.println("所用时间="+(endTime-startTime)+"ms"+" 最终点赞次数="+clickResource.longAccumulator); } } // 所用时间=150ms 最终点赞次数=100000000
LongAccumulator 和 LongAdder 底层调用的方法相同,主要区别是,LongAdder只能初始为0,且只能做加法操作,LongAccumulator 能初始成任何数
需要注意的是:
LongAdder 和 LongAccumulator 并不保证最终获取的数据是完全准确无误的,也许会有少许偏差,但在高并发的点赞场景下牺牲少量的数据精确性来保证高性能是完全能接受的
以上就是Java在高并发场景下实现点赞计数器的详细内容,更多关于Java点赞计数器的资料请关注脚本之家其它相关文章!
相关文章
利用JSONObject.toJSONString()包含或排除指定的属性
这篇文章主要介绍了利用JSONObject.toJSONString()包含或排除指定的属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-03-03
最新评论