Java在高并發(fā)場(chǎng)景下實(shí)現(xiàn)點(diǎn)贊計(jì)數(shù)器
點(diǎn)贊計(jì)數(shù)器的本質(zhì)就是對(duì)某個(gè)變量在高并發(fā)情況下的修改,volatile關(guān)鍵字只能保證變量的可見(jià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萬(wàn) 次, 求準(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ù)=1000000002. 使用 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萬(wàn) 次, 求準(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ù)=1000000003.使用 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萬(wàn) 次, 求準(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ù)=1000000004. 使用 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萬(wàn) 次, 求準(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ù)=100000000LongAccumulator 和 LongAdder 底層調(diào)用的方法相同,主要區(qū)別是,LongAdder只能初始為0,且只能做加法操作,LongAccumulator 能初始成任何數(shù)
需要注意的是:
LongAdder 和 LongAccumulator 并不保證最終獲取的數(shù)據(jù)是完全準(zhǔn)確無(wú)誤的,也許會(huì)有少許偏差,但在高并發(fā)的點(diǎn)贊場(chǎng)景下?tīng)奚倭康臄?shù)據(jù)精確性來(lái)保證高性能是完全能接受的
以上就是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)為了簡(jiǎn)化我們的開(kāi)發(fā),已經(jīng)提供了非常多場(chǎng)景的Starter來(lái)為我們使用,即便如此,也無(wú)法全面的滿足我們實(shí)際工作中的開(kāi)發(fā)場(chǎng)景,這時(shí)我們就需要自定義實(shí)現(xiàn)定制化的Starter2023-02-02
Spring使用@Filter注解創(chuàng)建自定義過(guò)濾器
Spring 中鮮為人知但非常有用的注解之一是 @Filter,它支持自定義過(guò)濾器,下面我們就來(lái)深入研究一下如何使用 Spring 的 @Filter 注解來(lái)創(chuàng)建自定義過(guò)濾器吧2023-11-11
Java調(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問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
Eclipse中引入com.sun.image.codec.jpeg包報(bào)錯(cuò)的完美解決辦法
Java開(kāi)發(fā)中對(duì)圖片的操作需要引入 com.sun.image.codec.jpeg,但有時(shí)引入這個(gè)包會(huì)報(bào)錯(cuò),利用下面的操作可以完成解決這個(gè)問(wèn)題2018-02-02

