日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DCL并非单例模式专用

發(fā)布時(shí)間:2023/12/2 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DCL并非单例模式专用 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  我相信大家都很熟悉DCL,對(duì)于缺少實(shí)踐經(jīng)驗(yàn)的程序開發(fā)人員來說,DCL的學(xué)習(xí)基本限制在單例模式,但我發(fā)現(xiàn)在高并發(fā)場(chǎng)景中會(huì)經(jīng)常遇到需要用到DCL的場(chǎng)景,但并非用做單例模式,其實(shí)DCL的核心思想和CopyOnWrite很相似,就是在需要的時(shí)候才加鎖;為了說明這個(gè)觀點(diǎn),我先把單例的經(jīng)典代碼防止如下:

  先說明幾個(gè)關(guān)鍵詞:

  volatile:保證線程的可見性,有序性;這兩點(diǎn)非常重要,可見性讓線程可以馬上獲釋主存變化,二有序性避免指令重排序出現(xiàn)問題;

public class Singleton {//通過volatile關(guān)鍵字來確保安全private volatile static Singleton singleton;private Singleton(){}public static Singleton getInstance(){if(singleton == null){synchronized (Singleton.class){if(singleton == null){singleton = new Singleton();}}}return singleton;} }

  大家可以知道,這段代碼是沒有性能瓶頸的線程安全(當(dāng)然,用了volatile是有一定的性能影響,但起碼不需要競(jìng)爭(zhēng)鎖);這代碼只會(huì)在需要的時(shí)候才加鎖,這就是DCL的需要時(shí)加鎖的特性,由第一個(gè)檢查check保證(也就是if (singleton == null));

  但DCL的需要時(shí)才加鎖的魅力不僅僅如此場(chǎng)景而已,我們看一個(gè)需求:一個(gè)不要求實(shí)時(shí)性的更新,所有線程公用一個(gè)資源,而且只有滿足某個(gè)條件的時(shí)候才更新,那么多線程需要訪問緩存時(shí),是否需要加鎖呢?不需要的,看如下代碼:

?

private static volatile JSONArray cache = new JSONArray(Collections.synchronizedList(new LinkedList<>()));public static int updateAeProduct(JSONObject aeProduct,String productId,boolean isFlush){JSONObject task = new JSONObject();String whereStr ="{\"productId\": {\"operation\": \"eq\", \"value\":\""+productId+"\" },\"provider\":{\"operation\": \"eq\", \"value\":\"aliExpress\" }}";task.put("where",JSON.parseObject(whereStr));task.put("params",aeProduct);cache.add(task);if(cache.size()>2 ||isFlush){ // 爭(zhēng)奪更新權(quán)JSONArray temp=cache;synchronized (updateLock){if(temp==cache&&cache.contains(task)){cache = new JSONArray(Collections.synchronizedList(new LinkedList<>()));}else {return 1;}} // 擁有更新權(quán)的繼續(xù)更新try {Map<String,String> headers = new HashMap<>();headers.put("Content-Type","application/json");String response = HttpUtils.post(updateapi,temp.toJSONString(),headers);JSONObject result = JSON.parseObject(response);if(result!=null&&"Success".equals(result.getString("msg"))){ // System.out.println("=========================完成一次批量存儲(chǔ),成功Flush:"+temp.size()); }} catch (Exception e) {System.out.println("更新丟失,策略補(bǔ)救");e.printStackTrace();}}return 1;}

  這樣保證了性能,也做到了緩存的線程安全;這就是單例的厲害;我在項(xiàng)目中經(jīng)常遇到該類場(chǎng)景,下面給出一個(gè)任務(wù)計(jì)時(shí)器的代碼:

package com.mobisummer.spider.master.component;import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong;public class RateCalculator {ConcurrentHashMap<String,AtomicLong> taskInfo = new ConcurrentHashMap();volatile boolean isStart =false;Object lock = new Object();AtomicLong allCount = new AtomicLong();private ScheduledExecutorService scheduledThreadPool;public void consume(Long num,String taskId){if(taskInfo.containsKey(taskId)){taskInfo.get(taskId).addAndGet(num);}else {calculateTask(num,taskId);}allCount.addAndGet(num);calculateAll(num,taskId);}/*** 計(jì)算任務(wù)* @param num* @param taskId*/private void calculateTask(Long num,String taskId){synchronized (lock){if(taskInfo.containsKey(taskId)){return;}else {taskInfo.put(taskId,new AtomicLong());Thread countor = new Thread(new Runnable() {@Overridepublic void run() {while (true){double startTime =System.currentTimeMillis();double startCount = taskInfo.get(taskId).get();try {Thread.sleep(10000);} catch (InterruptedException e) {System.out.println("計(jì)數(shù)器失效");}double endTime =System.currentTimeMillis();double endCount = taskInfo.get(taskId).get();double percent =(endCount-startCount)/((endTime - startTime)/1000); // System.out.println("目前總成功爬取速率:==========="+percent+"=======目前處理總數(shù)========:"+allCount);System.out.println("目前"+taskId+"成功爬取速率:==========="+percent+"=======目前"+taskId+"處理總數(shù)========:"+endCount);}}});countor.start();}}}/*** 計(jì)算所有任務(wù)* @param num* @param taskId*/private void calculateAll(Long num,String taskId){if(isStart){return;}else {synchronized (this){if(isStart){return;}else {isStart =true;Thread countor = new Thread(new Runnable() {@Overridepublic void run() {while (true){double startTime =System.currentTimeMillis();double startCount = allCount.get();try {Thread.sleep(10000);} catch (InterruptedException e) {System.out.println("計(jì)數(shù)器失效");}double endTime =System.currentTimeMillis();double endCount = allCount.get();double percent =(endCount-startCount)/((endTime - startTime)/1000);System.out.println("目前總成功爬取速率:==========="+percent+"=======目前處理總數(shù)========:"+allCount); // System.out.println("目前"+taskId+"成功爬取速率:==========="+percent+"=======目前"+taskId+"處理總數(shù)========:"+allCount); }}});countor.start();}}}} }

?

  同樣的,線程安全的雙重檢測(cè),這就是DCL的魅力;

轉(zhuǎn)載于:https://www.cnblogs.com/iCanhua/p/9532396.html

總結(jié)

以上是生活随笔為你收集整理的DCL并非单例模式专用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。