最近在繼續學習Go語言的過程中,發現了一個比較神奇的的對象sync.Once
,顧名思義,就是執行一次。官方定義的如下:Once是一個只執行一個動作的對象
,看包名sync
知道這是在併發使用場景。
基礎使用方法如下:
// TestOnceSimple once對象簡單測試
// @Description:
// @param t
//
func TestOnceSimple(t *testing.T) {
var once sync.Once
for i := 0; i < 10; i++ {
go once.Do(func() {
log.Println("執行一次")
})
}
time.Sleep(time.Second)
}
for
循環裏面分別異步執行了10次,但是最終控制檯展示如下:
=== RUN TestOnceSimple
2022/06/19 16:39:08 執行一次
--- PASS: TestOnceSimple (1.00s)
PASS
目前使用到場景中就是在各種配置進行初始化的時候,以防止多個異步同時來執行初始化任務導致異常。比如說,我再使用Redis連接池的時候,首先需要初始化連接池,通常需要一個方法來完成這個過程,大部分時候需要顯式調用,除非這個池對於我們項目來講是基礎的功能,程序啓動的時候就需要初始化。
這個在我寫Java的過程中,用到的HTTP的連接池和MySQL的連接池,而後者就屬於需要用的時候的再初始化的場景。還有一種方式,我們可以使用Java單例模式中的懶漢式的解決這個問題。但是我們如果在測試過程中使用不同的對象池的時候,這種方式又顯得比較死板不夠靈活。
所以在平時處理這種情況的時候,通常我會使用synchronized
或者java.util.concurrent.locks.ReentrantLock
等concurrent
包裏面的工具類完成這個需求。具體代碼可參考Java單例的懶漢式的實現以及我之前的文章。
之前我對照Go語言的go
異步關鍵字寫了Java自定義異步功能實踐,寫了一個Java版本的fun
異步關鍵字。這次我自然計劃要抄一下sync.Once
設計。
下面是我的經過自己嘗試寫了一個簡版的:
static Vector<Integer> ones = new Vector<>();
static ReentrantLock lock = new ReentrantLock();
/**
* 線程安全單次執行,仿照Go語言的once方法
*
* @param v
*/
public static void once(Closure v) {
try {
lock.lock();
int code = v.hashCode();
if (!ones.contains(code)) {
ones.add(code);
v.call();
}
} catch (Exception e) {
logger.warn("once執行方法失敗", e);
} finally {
lock.unlock();
}
}
下面是測試代碼:
package com.okcoin.hickwall.presses
import com.okcoin.hickwall.presses.funtester.httpclient.FunHttp
class OnceTest extends FunHttp {
public static void main(String[] args) {
def test = {
output("FunTester")
}
10.times {
fun {
once(test)
}
}
}
}
控制檯輸出:
16:56:22 main 守護線程:Deamon開啓!
16:56:22 F-1 FunTester
16:56:23 Deamon 異步線程池等待執行1次耗時:6 ms
16:56:23 Deamon 異步線程池關閉!
從上面內容我們看到,雖然異步執行了10次,但是隻有一次真正執行了,實現了預期的需求。
Fun·BUG挖掘機·性能征服者·頭頂鍋蓋·Tester
- 性能測試專題
- Java、Groovy、Go、Python
- 單測&白盒
- FunTester社羣風采
- 測試理論雞湯
- 接口功能測試專題
- FunTester視頻專題
- 案例分享:方案、BUG、爬蟲
- UI自動化專題
- 測試工具專題
閱讀原文,跳轉我的倉庫地址