Java在高并發(fā)場(chǎng)景下實(shí)現(xiàn)點(diǎn)贊計(jì)數(shù)器
點(diǎn)贊計(jì)數(shù)器的本質(zhì)就是對(duì)某個(gè)變量在高并發(fā)情況下的修改,volatile關(guān)鍵字只能保證變量的可見性和有序性,不能保證其原子性
使用方案有如下選擇:
1. Synchronized 關(guān)鍵字
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個(gè)線程, 每個(gè)線程點(diǎn)贊 100萬 次, 求準(zhǔn)確率和效率 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("所用時(shí)間="+(endTime-startTime)+"ms"+" 最終點(diǎn)贊次數(shù)="+clickResource.number); } } // synchronized 悲觀鎖 // 所用時(shí)間=2258ms 最終點(diǎn)贊次數(shù)=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個(gè)線程, 每個(gè)線程點(diǎn)贊 100萬 次, 求準(zhǔn)確率和效率 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("所用時(shí)間="+(endTime-startTime)+"ms"+" 最終點(diǎn)贊次數(shù)="+clickResource.atomicLong); } } // CAS - 輕量級(jí)鎖 - 調(diào)用 Unsafe 類的 cas 方法 - 底層操作系統(tǒng)原子指令 // 所用時(shí)間=1177ms 最終點(diǎn)贊次數(shù)=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個(gè)線程, 每個(gè)線程點(diǎn)贊 100萬 次, 求準(zhǔn)確率和效率 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("所用時(shí)間="+(endTime-startTime)+"ms"+" 最終點(diǎn)贊次數(shù)="+clickResource.longAdder); } } //所用時(shí)間=141ms 最終點(diǎn)贊次數(shù)=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個(gè)線程, 每個(gè)線程點(diǎn)贊 100萬 次, 求準(zhǔn)確率和效率 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("所用時(shí)間="+(endTime-startTime)+"ms"+" 最終點(diǎn)贊次數(shù)="+clickResource.longAccumulator); } } // 所用時(shí)間=150ms 最終點(diǎn)贊次數(shù)=100000000
LongAccumulator 和 LongAdder 底層調(diào)用的方法相同,主要區(qū)別是,LongAdder只能初始為0,且只能做加法操作,LongAccumulator 能初始成任何數(shù)
需要注意的是:
LongAdder 和 LongAccumulator 并不保證最終獲取的數(shù)據(jù)是完全準(zhǔn)確無誤的,也許會(huì)有少許偏差,但在高并發(fā)的點(diǎn)贊場(chǎng)景下犧牲少量的數(shù)據(jù)精確性來保證高性能是完全能接受的
以上就是Java在高并發(fā)場(chǎng)景下實(shí)現(xiàn)點(diǎn)贊計(jì)數(shù)器的詳細(xì)內(nèi)容,更多關(guān)于Java點(diǎn)贊計(jì)數(shù)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot自定義Starter與自動(dòng)配置實(shí)現(xiàn)方法詳解
在Spring Boot官網(wǎng)為了簡化我們的開發(fā),已經(jīng)提供了非常多場(chǎng)景的Starter來為我們使用,即便如此,也無法全面的滿足我們實(shí)際工作中的開發(fā)場(chǎng)景,這時(shí)我們就需要自定義實(shí)現(xiàn)定制化的Starter2023-02-02Spring使用@Filter注解創(chuàng)建自定義過濾器
Spring 中鮮為人知但非常有用的注解之一是 @Filter,它支持自定義過濾器,下面我們就來深入研究一下如何使用 Spring 的 @Filter 注解來創(chuàng)建自定義過濾器吧2023-11-11Java調(diào)用C++程序的實(shí)現(xiàn)方式
這篇文章主要介紹了Java調(diào)用C++程序的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02解決maven clean報(bào)錯(cuò):Failed to delete xxxxx\t
這篇文章主要介紹了解決maven clean報(bào)錯(cuò):Failed to delete xxxxx\target\xxxx.jar問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Eclipse中引入com.sun.image.codec.jpeg包報(bào)錯(cuò)的完美解決辦法
Java開發(fā)中對(duì)圖片的操作需要引入 com.sun.image.codec.jpeg,但有時(shí)引入這個(gè)包會(huì)報(bào)錯(cuò),利用下面的操作可以完成解決這個(gè)問題2018-02-02