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

