自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

讓人討厭的多線程代碼,性能怎么優(yōu)化!

開(kāi)發(fā) 前端
Java中有兩種加鎖方式,一種是使用synchronized關(guān)鍵字,另外一種是concurrent包下面的Lock。

Java 中最煩人的,就是多線程,一不小心,代碼寫(xiě)的比單線程還慢,這就讓人非常尷尬。

通常情況下,我們會(huì)使用 ThreadLocal 實(shí)現(xiàn)線程封閉,比如避免 SimpleDateFormat 在并發(fā)環(huán)境下所引起的一些不一致情況。其實(shí)還有一種解決方式。通過(guò)對(duì)parse方法進(jìn)行加鎖,也能保證日期處理類的正確運(yùn)行,代碼如圖。

圖片

1. 鎖很壞

但是,鎖這個(gè)東西,很壞。

所以,鎖對(duì)性能的影響,是非常大的。對(duì)資源加鎖以后,資源就被加鎖的線程所獨(dú)占,其他的線程就只能排隊(duì)等待這個(gè)鎖。此時(shí),程序由并行執(zhí)行,變相的變成了順序執(zhí)行,執(zhí)行速度自然就降低了。

下面是開(kāi)啟了50個(gè)線程,使用ThreadLocal和同步鎖方式性能的一個(gè)對(duì)比。

Benchmark                                 Mode  Cnt     Score      Error   Units
SynchronizedNormalBenchmark.sync thrpt 10 2554.628 ± 5098.059 ops/ms
SynchronizedNormalBenchmark.threadLocal thrpt 10 3750.902 ± 103.528 ops/ms
========去掉業(yè)務(wù)影響========
Benchmark Mode Cnt Score Error Units
SynchronizedNormalBenchmark.sync thrpt 10 26905.514 ± 1688.600 ops/ms
SynchronizedNormalBenchmark.threadLocal thrpt 10 7041876.244 ± 355598.686 ops/ms

可以看到,使用同步鎖的方式,性能是比較低的。如果去掉業(yè)務(wù)本身邏輯的影響(刪掉執(zhí)行邏輯),這個(gè)差異會(huì)更大。代碼執(zhí)行的次數(shù)越多,鎖的累加影響越大,對(duì)鎖本身的速度優(yōu)化,是非常重要的。

我們都知道,Java 中有兩種加鎖的方式,一種就是常見(jiàn)的synchronized 關(guān)鍵字,另外一種,就是使用 concurrent 包里面的 Lock。針對(duì)于這兩種鎖,JDK 自身做了很多的優(yōu)化,它們的實(shí)現(xiàn)方式也是不同的。

2. synchronied原理

synchronized關(guān)鍵字給代碼或者方法上鎖時(shí),都有顯示的或者隱藏的上鎖對(duì)象。當(dāng)一個(gè)線程試圖訪問(wèn)同步代碼塊時(shí),它首先必須得到鎖,退出或拋出異常時(shí)必須釋放鎖。

  • 給普通方法加鎖時(shí),上鎖的對(duì)象是this
  • 給靜態(tài)方法加鎖時(shí),鎖的是class對(duì)象。
  • 給代碼塊加鎖,可以指定一個(gè)具體的對(duì)象作為鎖

monitor,在操作系統(tǒng)里,其實(shí)就叫做管程。

那么,synchronized 在字節(jié)碼中,是怎么體現(xiàn)的呢?參照下面的代碼,在命令行執(zhí)行??javac??,然后再執(zhí)行??javap -v -p??,就可以看到它具體的字節(jié)碼??梢钥吹剑谧止?jié)碼的體現(xiàn)上,它只給方法加了一個(gè)flag:??ACC_SYNCHRONIZED??。

synchronized void syncMethod(){
System.out.println("syncMethod");
}
======字節(jié)碼=====
synchronized void syncMethod();
descriptor: ()V
flags: ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #4
3: ldc #5
5: invokevirtual #6
8: return

我們?cè)賮?lái)看下同步代碼塊的字節(jié)碼??梢钥吹剑止?jié)碼是通過(guò)monitorenter和monitorexit兩個(gè)指令進(jìn)行控制的。

void syncBlock(){
synchronized (Test.class){
}
}
======字節(jié)碼======
void syncBlock();
descriptor: ()V
flags:
Code:
stack=2, locals=3, args_size=1
0: ldc #2
2: dup
3: astore_1
4: monitorenter
5: aload_1
6: monitorexit
7: goto 15
10: astore_2
11: aload_1
12: monitorexit
13: aload_2
14: athrow
15: return
Exception table:
from to target type
5 7 10 any
10 13 10 any

這兩者雖然顯示效果不同,但他們都是通過(guò)monitor來(lái)實(shí)現(xiàn)同步的。我們可以通過(guò)下面這張圖,來(lái)看一下monitor的原理。

注意了,下面是面試題目高發(fā)地。

圖片

如圖所示,我們可以把運(yùn)行時(shí)的對(duì)象鎖抽象的分成三部分。其中,EntrySet 和WaitSet 是兩個(gè)隊(duì)列,中間虛線部分是當(dāng)前持有鎖的線程。我們可以想象一下線程的執(zhí)行過(guò)程。

當(dāng)?shù)谝粋€(gè)線程到來(lái)時(shí),發(fā)現(xiàn)并沒(méi)有線程持有對(duì)象鎖,它會(huì)直接成為活動(dòng)線程,進(jìn)入 RUNNING 狀態(tài)。

接著又來(lái)了三個(gè)線程,要爭(zhēng)搶對(duì)象鎖。此時(shí),這三個(gè)線程發(fā)現(xiàn)鎖已經(jīng)被占用了,就先進(jìn)入 EntrySet 緩存起來(lái),進(jìn)入 BLOCKED 狀態(tài)。此時(shí),從??jstack??命令,可以看到他們展示的信息都是??waiting for monitor entry??。

"http-nio-8084-exec-120" #143 daemon prio=5 os_prio=31 cpu=122.86ms elapsed=317.88s tid=0x00007fedd8381000 nid=0x1af03 waiting for monitor entry  [0x00007000150e1000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.io.BufferedInputStream.read(java.base@13.0.1/BufferedInputStream.java:263)
- waiting to lock <0x0000000782e1b590> (a java.io.BufferedInputStream)
at org.apache.commons.httpclient.HttpParser.readRawLine(HttpParser.java:78)
at org.apache.commons.httpclient.HttpParser.readLine(HttpParser.java:106)
at org.apache.commons.httpclient.HttpConnection.readLine(HttpConnection.java:1116)
at org.apache.commons.httpclient.HttpMethodBase.readStatusLine(HttpMethodBase.java:1973)
at org.apache.commons.httpclient.HttpMethodBase.readResponse(HttpMethodBase.java:1735)

處于活動(dòng)狀態(tài)的線程,執(zhí)行完畢退出了;或者由于某種原因執(zhí)行了wait 方法,釋放了對(duì)象鎖,就會(huì)進(jìn)入 WaitSet 隊(duì)列。這就是在調(diào)用??wait??之前,需要先獲得對(duì)象鎖的原因。就像下面的代碼:

synchronized (lock){
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

此時(shí),jstack顯示的線程狀態(tài)是 WAITING 狀態(tài),而原因是in Object.wait()。

"wait-demo" #12 prio=5 os_prio=31 cpu=0.14ms elapsed=12.58s tid=0x00007fb66609e000 nid=0x6103 in Object.wait()  [0x000070000f2bd000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(java.base@13.0.1/Native Method)
- waiting on <0x0000000787b48300> (a java.lang.Object)
at java.lang.Object.wait(java.base@13.0.1/Object.java:326)
at WaitDemo.lambda$main$0(WaitDemo.java:7)
- locked <0x0000000787b48300> (a java.lang.Object)
at WaitDemo$$Lambda$14/0x0000000800b44840.run(Unknown Source)
at java.lang.Thread.run(java.base@13.0.1/Thread.java:830)

發(fā)生了這兩種情況,都會(huì)造成對(duì)象鎖的釋放。進(jìn)而導(dǎo)致 EntrySet里的線程重新?tīng)?zhēng)搶對(duì)象鎖,成功搶到鎖的線程成為活動(dòng)線程,這是一個(gè)循環(huán)的過(guò)程。

那 WaitSet 中的線程是如何再次被激活的呢?接下來(lái),在某個(gè)地方,執(zhí)行了鎖的 notify 或者 notifyAll 命令,會(huì)造成WaitSet中 的線程,轉(zhuǎn)移到 EntrySet 中,重新進(jìn)行鎖的爭(zhēng)奪。

如此周而復(fù)始,線程就可按順序排隊(duì)執(zhí)行。

3. 分級(jí)鎖

JDK1.8中,synchronized 的速度已經(jīng)有了顯著的提升。那它都做了哪些優(yōu)化呢?答案就是分級(jí)鎖。JVM會(huì)根據(jù)使用情況,對(duì)synchronized 的鎖,進(jìn)行升級(jí),它大體可以按照下面的路徑:偏向鎖->輕量級(jí)鎖->重量級(jí)鎖。

鎖只能升級(jí),不能降級(jí),所以一旦升級(jí)為重量級(jí)鎖,就只能依靠操作系統(tǒng)進(jìn)行調(diào)度。

和鎖升級(jí)關(guān)系最大的就是對(duì)象頭里的 MarkWord,它包含Thread ID、Age、Biased、Tag四個(gè)部分。其中,Biased 有1bit大小,Tag 有2bit,鎖升級(jí)就是靠判斷Thread Id、Biased、Tag等三個(gè)變量值來(lái)進(jìn)行的。

偏向鎖

在只有一個(gè)線程使用了鎖的情況下,偏向鎖能夠保證更高的效率。

具體過(guò)程是這樣的。當(dāng)?shù)谝粋€(gè)線程第一次訪問(wèn)同步塊時(shí),會(huì)先檢測(cè)對(duì)象頭Mark Word中的標(biāo)志位Tag是否為01,以此判斷此時(shí)對(duì)象鎖是否處于無(wú)鎖狀態(tài)或者偏向鎖狀態(tài)(匿名偏向鎖)。

01也是鎖默認(rèn)的狀態(tài),線程一旦獲取了這把鎖,就會(huì)把自己的線程ID寫(xiě)到MarkWord中。在其他線程來(lái)獲取這把鎖之前,鎖都處于偏向鎖狀態(tài)。

輕量級(jí)鎖

當(dāng)下一個(gè)線程參與到偏向鎖競(jìng)爭(zhēng)時(shí),會(huì)先判斷 MarkWord 中保存的線程 ID 是否與這個(gè)線程 ID 相等,如果不相等,會(huì)立即撤銷偏向鎖,升級(jí)為輕量級(jí)鎖。

輕量級(jí)鎖的獲取是怎么進(jìn)行的呢?它們使用的是自旋方式。

參與競(jìng)爭(zhēng)的每個(gè)線程,會(huì)在自己的線程棧中生成一個(gè) LockRecord ( LR ),然后每個(gè)線程通過(guò) CAS (自旋)的方式,將鎖對(duì)象頭中的 MarkWord 設(shè)置為指向自己的 LR 的指針,哪個(gè)線程設(shè)置成功,就意味著哪個(gè)線程獲得鎖。

當(dāng)鎖處于輕量級(jí)鎖的狀態(tài)時(shí),就不能夠再通過(guò)簡(jiǎn)單的對(duì)比Tag的值進(jìn)行判斷,每次對(duì)鎖的獲取,都需要通過(guò)自旋。

當(dāng)然,自旋也是面向不存在鎖競(jìng)爭(zhēng)的場(chǎng)景,比如一個(gè)線程運(yùn)行完了,另外一個(gè)線程去獲取這把鎖。但如果自旋失敗達(dá)到一定的次數(shù),鎖就會(huì)膨脹為重量級(jí)鎖。

重量級(jí)鎖

重量級(jí)鎖即為我們對(duì)synchronized的直觀認(rèn)識(shí),這種情況下,線程會(huì)掛起,進(jìn)入到操作系統(tǒng)內(nèi)核態(tài),等待操作系統(tǒng)的調(diào)度,然后再映射回用戶態(tài)。系統(tǒng)調(diào)用是昂貴的,重量級(jí)鎖的名稱由此而來(lái)。

如果系統(tǒng)的共享變量競(jìng)爭(zhēng)非常激烈,鎖會(huì)迅速膨脹到重量級(jí)鎖,這些優(yōu)化就名存實(shí)亡。如果并發(fā)非常嚴(yán)重,可以通過(guò)參數(shù)??-XX:-UseBiasedLocking??禁用偏向鎖,理論上會(huì)有一些性能提升,但實(shí)際上并不確定。

4. Lock

在 concurrent 包里,我們能夠發(fā)現(xiàn)ReentrantLock和ReentrantReadWriteLock兩個(gè)類。Reentrant就是可重入的意思,它們和synchronized關(guān)鍵字一樣,都是可重入鎖。

這里有必要解釋一下可重入這個(gè)概念,因?yàn)樵诿嬖嚨臅r(shí)候經(jīng)常被問(wèn)到。它的意思是,一個(gè)線程運(yùn)行時(shí),可以多次獲取同一個(gè)對(duì)象鎖。這是因?yàn)镴ava的鎖是基于線程的,而不是基于調(diào)用的。比如下面這段代碼,由于方法a、b、c鎖的都是當(dāng)前的this,線程在調(diào)用a方法的時(shí)候,就不需要多次獲取對(duì)象鎖。

public synchronized void a(){
b();
}
public synchronized void b(){
c();
}
public synchronized void c(){
}

主要方法

LOCK是基于AQS(AbstractQueuedSynchronizer)實(shí)現(xiàn)的,而AQS 是基于 volitale 和 CAS 實(shí)現(xiàn)的。關(guān)于CAS,我們將在下一課時(shí)講解。

Lock與synchronized的使用方法不同,它需要手動(dòng)加鎖,然后在finally中解鎖。Lock接口比synchronized靈活性要高,我們來(lái)看一下幾個(gè)關(guān)鍵方法。

  • lock: lock方法和synchronized沒(méi)什么區(qū)別,如果獲取不到鎖,都會(huì)被阻塞
  • tryLock: 此方法會(huì)嘗試獲取鎖,不管能不能獲取到鎖,都會(huì)立即返回,不會(huì)阻塞。它是有返回值的,獲取到鎖就會(huì)返回true
  • tryLock(long time, TimeUnit unit): 
  • lockInterruptibly: 與lock類似,但是可以鎖等待可以被中斷,中斷后返回InterruptedException

一般情況下,使用lock方法就可以。但如果業(yè)務(wù)請(qǐng)求要求響應(yīng)及時(shí),那使用帶超時(shí)時(shí)間的tryLock是更好的選擇:我們的業(yè)務(wù)可以直接返回失敗,而不用進(jìn)行阻塞等待。tryLock這種優(yōu)化手段,采用降低請(qǐng)求成功率的方式,來(lái)保證服務(wù)的可用性,高并發(fā)場(chǎng)景下經(jīng)常被使用。

讀寫(xiě)鎖

但對(duì)于有些業(yè)務(wù)來(lái)說(shuō),使用Lock這種粗粒度的鎖還是太慢了。比如,對(duì)于一個(gè)HashMap來(lái)說(shuō),某個(gè)業(yè)務(wù)是讀多寫(xiě)少的場(chǎng)景,這個(gè)時(shí)候,如果給讀操作也加上和寫(xiě)操作一樣的鎖的話,效率就會(huì)很慢。

ReentrantReadWriteLock是一種讀寫(xiě)分離的鎖,它允許多個(gè)讀線程同時(shí)進(jìn)行,但讀和寫(xiě)、寫(xiě)和寫(xiě)是互斥的。使用方法如下所示,分別獲取讀寫(xiě)鎖,對(duì)寫(xiě)操作加寫(xiě)鎖,對(duì)讀操作加讀鎖,并在finally里釋放鎖即可。

ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Lock readLock = lock.readLock();
Lock writeLock = lock.writeLock();

public void put(K k, V v){
writeLock.lock();
try {
map.put(k, v);
} finally {
writeLock.unlock();
}
}
...

那么,除了ReadWriteLock,我們能有更快的讀寫(xiě)分離模式么?JDK1.8加入了哪個(gè)API?歡迎留言區(qū)評(píng)論。

公平鎖與非公平鎖

我們平常用到的鎖,都是非公平鎖??梢曰剡^(guò)頭來(lái)看一下monitor的原理。當(dāng)持有鎖的線程釋放鎖的時(shí)候,EntrySet里的線程就會(huì)爭(zhēng)搶這把鎖。這個(gè)爭(zhēng)搶的過(guò)程,是隨機(jī)的,也就是說(shuō)你并不知道哪個(gè)線程會(huì)獲取對(duì)象鎖,誰(shuí)搶到了就算誰(shuí)的。

這就有一定的概率,某個(gè)線程總是搶不到鎖,比如,線程通過(guò)setPriority 設(shè)置的比較低的優(yōu)先級(jí)。這個(gè)搶不到鎖的線程,就一直處于??饑餓??狀態(tài),這就是??線程饑餓??的概念。

公平鎖通過(guò)把隨機(jī)變成有序,可以解決這個(gè)問(wèn)題。synchronized沒(méi)有這個(gè)功能,在Lock中可以通過(guò)構(gòu)造參數(shù)設(shè)置成公平鎖,代碼如下。

public ReentrantReadWriteLock(boolean fair){
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}

由于所有的線程都需要排隊(duì),需要在多核的場(chǎng)景下維護(hù)一個(gè)同步隊(duì)列,在多個(gè)線程爭(zhēng)搶鎖的時(shí)候,吞吐量就很低。下面是20個(gè)并發(fā)之下鎖的JMH測(cè)試結(jié)果,可以看到,非公平鎖比公平鎖性能高出兩個(gè)數(shù)量級(jí)。

Benchmark                      Mode  Cnt      Score      Error   Units
FairVSNoFairBenchmark.fair thrpt 10 186.144 ± 27.462 ops/ms
FairVSNoFairBenchmark.nofair thrpt 10 35195.649 ± 6503.375 ops/ms

5. 鎖的優(yōu)化技巧

死鎖

我們可以先看一下鎖沖突最嚴(yán)重的一種情況:死鎖。下面這段示例代碼,兩個(gè)線程分別持有了對(duì)方所需要的鎖,進(jìn)入了相互等待的狀態(tài),就進(jìn)入了死鎖。面試中手寫(xiě)這段代碼的頻率,還是挺高的。

public class DeadLockDemo {
public static void main(String[] args){
Object object1 = new Object();
Object object2 = new Object();
Thread t1 = new Thread(() -> {
synchronized (object1) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object2) {
}
}
}, "deadlock-demo-1");

t1.start();
Thread t2 = new Thread(() -> {
synchronized (object2) {
synchronized (object1) {
}
}
}, "deadlock-demo-2");
t2.start();
}
}

使用我們上面提到的,帶超時(shí)時(shí)間的tryLock方法,有一方讓步,可以一定程度上避免死鎖。

優(yōu)化技巧

鎖的優(yōu)化理論其實(shí)很簡(jiǎn)單,那就是減少鎖的沖突。無(wú)論是鎖的讀寫(xiě)分離,還是分段鎖,本質(zhì)上都是為了避免多個(gè)線程同時(shí)獲取同一把鎖。我們可以總結(jié)一下優(yōu)化的一般思路:減少鎖的粒度、減少鎖持有的時(shí)間、鎖分級(jí)、鎖分離 、鎖消除、樂(lè)觀鎖、無(wú)鎖等。

圖片

減少鎖粒度

通過(guò)減小鎖的粒度,可以將沖突分散,減少?zèng)_突的可能,從而提高并發(fā)量。簡(jiǎn)單來(lái)說(shuō),就是把資源進(jìn)行抽象,針對(duì)每類資源使用單獨(dú)的鎖進(jìn)行保護(hù)。比如下面的代碼,由于list1和list2屬于兩類資源,就沒(méi)必要使用同一個(gè)對(duì)象鎖進(jìn)行處理。

public class LockLessDemo {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
public synchronized void addList1(String v){
this.list1.add(v);
}
public synchronized void addList2(String v){
this.list2.add(v);
}
}

可以創(chuàng)建兩個(gè)不同的鎖,改善情況如下:

public class LockLessDemo {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
final Object lock1 = new Object();
final Object lock2 = new Object();
public void addList1(String v){
synchronized (lock1) {
this.list1.add(v);
}
}
public void addList2(String v){
synchronized (lock2) {
this.list2.add(v);
}
}
}

減少鎖持有時(shí)間通過(guò)讓鎖資源盡快的釋放,減少鎖持有的時(shí)間,其他線程可更迅速的獲取鎖資源,進(jìn)行其他業(yè)務(wù)的處理。考慮到下面的代碼,由于slowMethod不在鎖的范圍內(nèi),占用的時(shí)間又比較長(zhǎng),可以把它移動(dòng)到synchronized代碼快外面,加速鎖的釋放。

public class LockTimeDemo {
List<String> list = new ArrayList<>();
final Object lock = new Object();
public void addList(String v){
synchronized (lock) {
slowMethod();
this.list.add(v);
}
}
public void slowMethod(){
}
}

?鎖分級(jí)鎖分級(jí)指的是我們文章開(kāi)始講解的synchronied鎖的鎖升級(jí),屬于JVM的內(nèi)部?jī)?yōu)化。它從偏向鎖開(kāi)始,逐漸會(huì)升級(jí)為輕量級(jí)鎖、重量級(jí)鎖,這個(gè)過(guò)程是不可逆的。

鎖分離我們?cè)谏厦嫣岬降淖x寫(xiě)鎖,就是鎖分離技術(shù)。這是因?yàn)?,讀操作一般是不會(huì)對(duì)資源產(chǎn)生影響的,可以并發(fā)執(zhí)行。寫(xiě)操作和其他操作是互斥的,只能排隊(duì)執(zhí)行。所以讀寫(xiě)鎖適合讀多寫(xiě)少的場(chǎng)景。

鎖消除通過(guò)JIT編譯器,JVM可以消除某些對(duì)象的加鎖操作。舉個(gè)例子,大家都知道StringBuffer和StringBuilder都是做字符串拼接的,而且前者是線程安全的。

但其實(shí),如果這兩個(gè)字符串拼接對(duì)象用在函數(shù)內(nèi),JVM通過(guò)逃逸分析分析這個(gè)對(duì)象的作用范圍就是在本函數(shù)中,就會(huì)把鎖的影響給消除掉。比如下面這段代碼,它和StringBuilder的效果是一樣的。

String m1(){
StringBuffer sb = new StringBuffer();
sb.append("");
return sb.toString();
}

End

Java中有兩種加鎖方式,一種是使用synchronized關(guān)鍵字,另外一種是concurrent包下面的Lock。本課時(shí),我們?cè)敿?xì)的了解了它們的一些特性,包括實(shí)現(xiàn)原理。下面對(duì)比如下:

類別

Synchronized

Lock

實(shí)現(xiàn)方式

monitor

AQS

底層細(xì)節(jié)

JVM優(yōu)化

Java API

分級(jí)鎖



功能特性

單一

豐富

鎖分離

無(wú)

讀寫(xiě)鎖

鎖超時(shí)

無(wú)

帶超時(shí)時(shí)間的tryLock

可中斷


lockInterruptibly

Lock的功能是比synchronized多的,能夠?qū)€程行為進(jìn)行更細(xì)粒度的控制。但如果只是用最簡(jiǎn)單的鎖互斥功能,建議直接使用synchronized。有兩個(gè)原因:

  • synchronized的編程模型更加簡(jiǎn)單,更易于使用
  • synchronized引入了偏向鎖,輕量級(jí)鎖等功能,能夠從JVM層進(jìn)行優(yōu)化,同時(shí),JIT編譯器也會(huì)對(duì)它執(zhí)行一些鎖消除動(dòng)作

多線程代碼好寫(xiě),但bug難找,希望你的代碼即干凈又強(qiáng)壯,兼高性能與高可靠于一身。

責(zé)任編輯:武曉燕 來(lái)源: 小姐姐味道
相關(guān)推薦

2024-01-22 09:16:47

多線程性能優(yōu)化

2022-04-11 09:58:07

數(shù)據(jù)庫(kù)SQL

2024-11-05 16:29:57

2010-01-28 09:55:05

性能優(yōu)化

2022-07-20 07:45:15

多線程程序性能

2024-09-29 09:27:10

2024-09-27 09:31:25

2019-12-26 09:56:34

Java多線程內(nèi)部鎖

2018-10-25 15:55:44

Java多線程鎖優(yōu)化

2022-03-04 06:46:30

Python代碼

2015-08-25 08:55:14

優(yōu)秀代碼基因

2025-02-25 12:00:00

Java線程開(kāi)發(fā)

2021-08-06 22:51:45

CPU限流容器

2010-03-17 19:24:38

Java多線程循環(huán)

2012-07-23 10:22:15

Python性能優(yōu)化優(yōu)化技巧

2009-11-27 13:24:20

PHP代碼性能優(yōu)化

2019-03-06 10:25:30

Web圖片優(yōu)化命令

2012-09-04 09:55:22

代碼抓狂的代碼開(kāi)發(fā)

2019-06-13 16:30:37

代碼Java編程語(yǔ)言

2011-06-14 11:14:10

性能優(yōu)化代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)