最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 高并發(fā)九:鎖的優(yōu)化和注意事項(xiàng)詳解

 更新時(shí)間:2016年09月12日 09:10:39   作者:Hosee  
本文主要介紹Java高并發(fā)鎖的優(yōu)化和注意事項(xiàng),這里整理了詳細(xì)的資料,并講解了 1. 鎖優(yōu)化的思路和方法 2. 虛擬機(jī)內(nèi)的鎖優(yōu)化 3. 一個(gè)錯(cuò)誤使用鎖的案例 4. ThreadLocal及其源碼分析等知識(shí),有需要的小伙伴可以參考下

摘要

本系列基于煉數(shù)成金課程,為了更好的學(xué)習(xí),做了系列的記錄。 本文主要介紹: 1. 鎖優(yōu)化的思路和方法 2. 虛擬機(jī)內(nèi)的鎖優(yōu)化 3. 一個(gè)錯(cuò)誤使用鎖的案例 4. ThreadLocal及其源碼分析

1. 鎖優(yōu)化的思路和方法

在[高并發(fā)Java 一] 前言中有提到并發(fā)的級(jí)別。

一旦用到鎖,就說明這是阻塞式的,所以在并發(fā)度上一般來說都會(huì)比無鎖的情況低一點(diǎn)。

這里提到的鎖優(yōu)化,是指在阻塞式的情況下,如何讓性能不要變得太差。但是再怎么優(yōu)化,一般來說性能都會(huì)比無鎖的情況差一點(diǎn)。

這里要注意的是,在[高并發(fā)Java 五] JDK并發(fā)包1中提到的ReentrantLock中的tryLock,偏向于一種無鎖的方式,因?yàn)樵趖ryLock判斷時(shí),并不會(huì)把自己掛起。

鎖優(yōu)化的思路和方法總結(jié)一下,有以下幾種。

  1. 減少鎖持有時(shí)間
  2. 減小鎖粒度
  3. 鎖分離
  4. 鎖粗化
  5. 鎖消除

1.1 減少鎖持有時(shí)間

public synchronized void syncMethod(){ 
 othercode1(); 
 mutextMethod(); 
 othercode2(); 
 }

像上述代碼這樣,在進(jìn)入方法前就要得到鎖,其他線程就要在外面等待。

這里優(yōu)化的一點(diǎn)在于,要減少其他線程等待的時(shí)間,所以,只用在有線程安全要求的程序上加鎖

public void syncMethod(){ 
 othercode1(); 
 synchronized(this)
 {
 mutextMethod(); 
 }
 othercode2(); 
 }

1.2 減小鎖粒度

將大對(duì)象(這個(gè)對(duì)象可能會(huì)被很多線程訪問),拆成小對(duì)象,大大增加并行度,降低鎖競(jìng)爭(zhēng)。降低了鎖的競(jìng)爭(zhēng),偏向鎖,輕量級(jí)鎖成功率才會(huì)提高。

最最典型的減小鎖粒度的案例就是ConcurrentHashMap。這個(gè)在[高并發(fā)Java 五] JDK并發(fā)包1有提到。

1.3 鎖分離

最常見的鎖分離就是讀寫鎖ReadWriteLock,根據(jù)功能進(jìn)行分離成讀鎖和寫鎖,這樣讀讀不互斥,讀寫互斥,寫寫互斥,即保證了線程安全,又提高了性能,具體也請(qǐng)查看[高并發(fā)Java 五] JDK并發(fā)包1。

讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離。

比如LinkedBlockingQueue 

從頭部取出,從尾部放數(shù)據(jù)。當(dāng)然也類似于[高并發(fā)Java 六] JDK并發(fā)包2中提到的ForkJoinPool中的工作竊取。

1.4 鎖粗化

通常情況下,為了保證多線程間的有效并發(fā),會(huì)要求每個(gè)線程持有鎖的時(shí)間盡量短,即在使用完公共資源后,應(yīng)該立即釋放鎖。只有這樣,等待在這個(gè)鎖上的其他線程才能盡早的獲得資源執(zhí)行任務(wù)。但是,凡事都有一個(gè)度,如果對(duì)同一個(gè)鎖不停的進(jìn)行請(qǐng)求、同步和釋放,其本身也會(huì)消耗系統(tǒng)寶貴的資源,反而不利于性能的優(yōu)化 。

舉個(gè)例子:

public void demoMethod(){ 
 synchronized(lock){ 
 //do sth. 
 } 
 //做其他不需要的同步的工作,但能很快執(zhí)行完畢 
 synchronized(lock){ 
 //do sth. 
 } 
 }

這種情況,根據(jù)鎖粗化的思想,應(yīng)該合并

public void demoMethod(){ 
 //整合成一次鎖請(qǐng)求 
 synchronized(lock){ 
 //do sth. 
 //做其他不需要的同步的工作,但能很快執(zhí)行完畢 
 }
 }

當(dāng)然這是有前提的,前提就是中間的那些不需要同步的工作是很快執(zhí)行完成的。
再舉一個(gè)極端的例子:

for(int i=0;i<CIRCLE;i++){ 
 synchronized(lock){ 
 
 } 
 }

在一個(gè)循環(huán)內(nèi)不同得獲得鎖。雖然JDK內(nèi)部會(huì)對(duì)這個(gè)代碼做些優(yōu)化,但是還不如直接寫成

synchronized(lock){ 
 for(int i=0;i<CIRCLE;i++){ 
 
 } 
 }

當(dāng)然如果有需求說,這樣的循環(huán)太久,需要給其他線程不要等待太久,那只能寫成上面那種。如果沒有這樣類似的需求,還是直接寫成下面那種比較好。

1.5 鎖消除

鎖消除是在編譯器級(jí)別的事情。

在即時(shí)編譯器時(shí),如果發(fā)現(xiàn)不可能被共享的對(duì)象,則可以消除這些對(duì)象的鎖操作。

也許你會(huì)覺得奇怪,既然有些對(duì)象不可能被多線程訪問,那為什么要加鎖呢?寫代碼時(shí)直接不加鎖不就好了。

但是有時(shí),這些鎖并不是程序員所寫的,有的是JDK實(shí)現(xiàn)中就有鎖的,比如Vector和StringBuffer這樣的類,它們中的很多方法都是有鎖的。當(dāng)我們?cè)谝恍┎粫?huì)有線程安全的情況下使用這些類的方法時(shí),達(dá)到某些條件時(shí),編譯器會(huì)將鎖消除來提高性能。

比如:

public static void main(String args[]) throws InterruptedException {
 long start = System.currentTimeMillis();
 for (int i = 0; i < 2000000; i++) {
 createStringBuffer("JVM", "Diagnosis");
 }
 long bufferCost = System.currentTimeMillis() - start;
 System.out.println("craeteStringBuffer: " + bufferCost + " ms");
 }

 public static String createStringBuffer(String s1, String s2) {
 StringBuffer sb = new StringBuffer();
 sb.append(s1);
 sb.append(s2);
 return sb.toString();
 }

上述代碼中的StringBuffer.append是一個(gè)同步操作,但是StringBuffer卻是一個(gè)局部變量,并且方法也并沒有把StringBuffer返回,所以不可能會(huì)有多線程去訪問它。
那么此時(shí)StringBuffer中的同步操作就是沒有意義的。

開啟鎖消除是在JVM參數(shù)上設(shè)置的,當(dāng)然需要在server模式下:

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

并且要開啟逃逸分析。 逃逸分析的作用呢,就是看看變量是否有可能逃出作用域的范圍。
比如上述的StringBuffer,上述代碼中craeteStringBuffer的返回是一個(gè)String,所以這個(gè)局部變量StringBuffer在其他地方都不會(huì)被使用。如果將craeteStringBuffer改成

public static StringBuffer craeteStringBuffer(String s1, String s2) {
 StringBuffer sb = new StringBuffer();
 sb.append(s1);
 sb.append(s2);
 return sb;
 }

那么這個(gè) StringBuffer被返回后,是有可能被任何其他地方所使用的(譬如被主函數(shù)將返回結(jié)果put進(jìn)map啊等等)。那么JVM的逃逸分析可以分析出,這個(gè)局部變量 StringBuffer逃出了它的作用域。

所以基于逃逸分析,JVM可以判斷,如果這個(gè)局部變量StringBuffer并沒有逃出它的作用域,那么可以確定這個(gè)StringBuffer并不會(huì)被多線程所訪問,那么就可以把這些多余的鎖給去掉來提高性能。

當(dāng)JVM參數(shù)為:

-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks

輸出:

craeteStringBuffer: 302 ms

JVM參數(shù)為:

-server -XX:+DoEscapeAnalysis -XX:-EliminateLocks

輸出:

craeteStringBuffer: 660 ms

顯然,鎖消除的效果還是很明顯的。

2. 虛擬機(jī)內(nèi)的鎖優(yōu)化

首先要介紹下對(duì)象頭,在JVM中,每個(gè)對(duì)象都有一個(gè)對(duì)象頭。

Mark Word,對(duì)象頭的標(biāo)記,32位(32位系統(tǒng))。

描述對(duì)象的hash、鎖信息,垃圾回收標(biāo)記,年齡

還會(huì)保存指向鎖記錄的指針,指向monitor的指針,偏向鎖線程ID等。

簡(jiǎn)單來說,對(duì)象頭就是要保存一些系統(tǒng)性的信息。

2.1 偏向鎖

所謂的偏向,就是偏心,即鎖會(huì)偏向于當(dāng)前已經(jīng)占有鎖的線程 。

大部分情況是沒有競(jìng)爭(zhēng)的(某個(gè)同步塊大多數(shù)情況都不會(huì)出現(xiàn)多線程同時(shí)競(jìng)爭(zhēng)鎖),所以可以通過偏向來提高性能。即在無競(jìng)爭(zhēng)時(shí),之前獲得鎖的線程再次獲得鎖時(shí),會(huì)判斷是否偏向鎖指向我,那么該線程將不用再次獲得鎖,直接就可以進(jìn)入同步塊。

偏向鎖的實(shí)施就是將對(duì)象頭Mark的標(biāo)記設(shè)置為偏向,并將線程ID寫入對(duì)象頭Mark

當(dāng)其他線程請(qǐng)求相同的鎖時(shí),偏向模式結(jié)束

JVM默認(rèn)啟用偏向鎖 -XX:+UseBiasedLocking

在競(jìng)爭(zhēng)激烈的場(chǎng)合,偏向鎖會(huì)增加系統(tǒng)負(fù)擔(dān)(每次都要加一次是否偏向的判斷)

偏向鎖的例子:

package test;

import java.util.List;
import java.util.Vector;

public class Test {
 public static List<Integer> numberList = new Vector<Integer>();

 public static void main(String[] args) throws InterruptedException {
 long begin = System.currentTimeMillis();
 int count = 0;
 int startnum = 0;
 while (count < 10000000) {
 numberList.add(startnum);
 startnum += 2;
 count++;
 }
 long end = System.currentTimeMillis();
 System.out.println(end - begin);
 }

}

Vector是一個(gè)線程安全的類,內(nèi)部使用了鎖機(jī)制。每次add都會(huì)進(jìn)行鎖請(qǐng)求。上述代碼只有main一個(gè)線程再反復(fù)add請(qǐng)求鎖。
使用如下的JVM參數(shù)來設(shè)置偏向鎖:

-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0
BiasedLockingStartupDelay表示系統(tǒng)啟動(dòng)幾秒鐘后啟用偏向鎖。默認(rèn)為4秒,原因在于,系統(tǒng)剛啟動(dòng)時(shí),一般數(shù)據(jù)競(jìng)爭(zhēng)是比較激烈的,此時(shí)啟用偏向鎖會(huì)降低性能。
由于這里為了測(cè)試偏向鎖的性能,所以把延遲偏向鎖的時(shí)間設(shè)置為0。

此時(shí)輸出為9209

下面關(guān)閉偏向鎖:

-XX:-UseBiasedLocking

輸出為9627

一般在無競(jìng)爭(zhēng)時(shí),啟用偏向鎖性能會(huì)提高5%左右。

2.2 輕量級(jí)鎖

Java的多線程安全是基于Lock機(jī)制實(shí)現(xiàn)的,而Lock的性能往往不如人意。

原因是,monitorenter與monitorexit這兩個(gè)控制多線程同步的bytecode原語,是JVM依賴操作系統(tǒng)互斥(mutex)來實(shí)現(xiàn)的。

互斥是一種會(huì)導(dǎo)致線程掛起,并在較短的時(shí)間內(nèi)又需要重新調(diào)度回原線程的,較為消耗資源的操作。

為了優(yōu)化Java的Lock機(jī)制,從Java6開始引入了輕量級(jí)鎖的概念。

輕量級(jí)鎖(Lightweight Locking)本意是為了減少多線程進(jìn)入互斥的幾率,并不是要替代互斥。

它利用了CPU原語Compare-And-Swap(CAS,匯編指令CMPXCHG),嘗試在進(jìn)入互斥前,進(jìn)行補(bǔ)救。

如果偏向鎖失敗,那么系統(tǒng)會(huì)進(jìn)行輕量級(jí)鎖的操作。它存在的目的是盡可能不用動(dòng)用操作系統(tǒng)層面的互斥,因?yàn)槟莻€(gè)性能會(huì)比較差。因?yàn)镴VM本身就是一個(gè)應(yīng)用,所以希望在應(yīng)用層面上就解決線程同步問題。

總結(jié)一下就是輕量級(jí)鎖是一種快速的鎖定方法,在進(jìn)入互斥之前,使用CAS操作來嘗試加鎖,盡量不要用操作系統(tǒng)層面的互斥,提高了性能。

那么當(dāng)偏向鎖失敗時(shí),輕量級(jí)鎖的步驟:

1.將對(duì)象頭的Mark指針保存到鎖對(duì)象中(這里的對(duì)象指的就是鎖住的對(duì)象,比如synchronized (this){},this就是這里的對(duì)象)。

lock->set_displaced_header(mark);

2.將對(duì)象頭設(shè)置為指向鎖的指針(在線程??臻g中)。

if (mark == (markOop) Atomic::cmpxchg_ptr(lock, obj()->mark_addr(),mark)) 
 {  
 TEVENT (slow_enter: release stacklock) ;  
 return ; 
 }

lock位于線程棧中。所以判斷一個(gè)線程是否持有這把鎖,只要判斷這個(gè)對(duì)象頭指向的空間是否在這個(gè)線程棧的地址空間當(dāng)中。
如果輕量級(jí)鎖失敗,表示存在競(jìng)爭(zhēng),升級(jí)為重量級(jí)鎖(常規(guī)鎖),就是操作系統(tǒng)層面的同步方法。在沒有鎖競(jìng)爭(zhēng)的情況,輕量級(jí)鎖減少傳統(tǒng)鎖使用OS互斥量產(chǎn)生的性能損耗。在競(jìng)爭(zhēng)非常激烈時(shí)(輕量級(jí)鎖總是失敗),輕量級(jí)鎖會(huì)多做很多額外操作,導(dǎo)致性能下降。

2.3 自旋鎖

當(dāng)競(jìng)爭(zhēng)存在時(shí),因?yàn)檩p量級(jí)鎖嘗試失敗,之后有可能會(huì)直接升級(jí)成重量級(jí)鎖動(dòng)用操作系統(tǒng)層面的互斥。也有可能再嘗試一下自旋鎖。

如果線程可以很快獲得鎖,那么可以不在OS層掛起線程,讓線程做幾個(gè)空操作(自旋),并且不停地嘗試拿到這個(gè)鎖(類似tryLock),當(dāng)然循環(huán)的次數(shù)是有限制的,當(dāng)循環(huán)次數(shù)達(dá)到以后,仍然升級(jí)成重量級(jí)鎖。所以在每個(gè)線程對(duì)于鎖的持有時(shí)間很少時(shí),自旋鎖能夠盡量避免線程在OS層被掛起。

JDK1.6中-XX:+UseSpinning開啟

JDK1.7中,去掉此參數(shù),改為內(nèi)置實(shí)現(xiàn)

如果同步塊很長(zhǎng),自旋失敗,會(huì)降低系統(tǒng)性能。如果同步塊很短,自旋成功,節(jié)省線程掛起切換時(shí)間,提升系統(tǒng)性能。

2.4 偏向鎖,輕量級(jí)鎖,自旋鎖總結(jié)

上述的鎖不是Java語言層面的鎖優(yōu)化方法,是內(nèi)置在JVM當(dāng)中的。

首先偏向鎖是為了避免某個(gè)線程反復(fù)獲得/釋放同一把鎖時(shí)的性能消耗,如果仍然是同個(gè)線程去獲得這個(gè)鎖,嘗試偏向鎖時(shí)會(huì)直接進(jìn)入同步塊,不需要再次獲得鎖。

而輕量級(jí)鎖和自旋鎖都是為了避免直接調(diào)用操作系統(tǒng)層面的互斥操作,因?yàn)閽炱鹁€程是一個(gè)很耗資源的操作。

為了盡量避免使用重量級(jí)鎖(操作系統(tǒng)層面的互斥),首先會(huì)嘗試輕量級(jí)鎖,輕量級(jí)鎖會(huì)嘗試使用CAS操作來獲得鎖,如果輕量級(jí)鎖獲得失敗,說明存在競(jìng)爭(zhēng)。但是也許很快就能獲得鎖,就會(huì)嘗試自旋鎖,將線程做幾個(gè)空循環(huán),每次循環(huán)時(shí)都不斷嘗試獲得鎖。如果自旋鎖也失敗,那么只能升級(jí)成重量級(jí)鎖。

可見偏向鎖,輕量級(jí)鎖,自旋鎖都是樂觀鎖。

3. 一個(gè)錯(cuò)誤使用鎖的案例

public class IntegerLock {
 static Integer i = 0;

 public static class AddThread extends Thread {
 public void run() {
 for (int k = 0; k < 100000; k++) {
 synchronized (i) {
  i++;
 }
 }
 }
 }

 public static void main(String[] args) throws InterruptedException {
 AddThread t1 = new AddThread();
 AddThread t2 = new AddThread();
 t1.start();
 t2.start();
 t1.join();
 t2.join();
 System.out.println(i);
 }
}

一個(gè)很初級(jí)的錯(cuò)誤在于,在 [高并發(fā)Java 七] 并發(fā)設(shè)計(jì)模式提到,Interger是final不變的,每次++后,會(huì)產(chǎn)生一個(gè)新的 Interger再賦給i,所以兩個(gè)線程爭(zhēng)奪的鎖是不同的。所以并不是線程安全的。

4. ThreadLocal及其源碼分析

這里來提ThreadLocal可能有點(diǎn)不合適,但是ThreadLocal是可以把鎖代替的方式。所以還是有必要提一下。

基本的思想就是,在一個(gè)多線程當(dāng)中需要把有數(shù)據(jù)沖突的數(shù)據(jù)加鎖,使用ThreadLocal的話,為每一個(gè)線程都提供一個(gè)對(duì)象實(shí)例。不同的線程只訪問自己的對(duì)象,而不訪問其他的對(duì)象。這樣鎖就沒有必要存在了。

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
 private static final SimpleDateFormat sdf = new SimpleDateFormat(
 "yyyy-MM-dd HH:mm:ss");

 public static class ParseDate implements Runnable {
 int i = 0;

 public ParseDate(int i) {
 this.i = i;
 }

 public void run() {
 try {
 Date t = sdf.parse("2016-02-16 17:00:" + i % 60);
 System.out.println(i + ":" + t);
 } catch (ParseException e) {
 e.printStackTrace();
 }
 }
 }

 public static void main(String[] args) {
 ExecutorService es = Executors.newFixedThreadPool(10);
 for (int i = 0; i < 1000; i++) {
 es.execute(new ParseDate(i));
 }
 }

}

由于SimpleDateFormat并不線程安全的,所以上述代碼是錯(cuò)誤的使用。最簡(jiǎn)單的方式就是,自己定義一個(gè)類去用synchronized包裝(類似于Collections.synchronizedMap)。這樣做在高并發(fā)時(shí)會(huì)有問題,對(duì) synchronized的爭(zhēng)用導(dǎo)致每一次只能進(jìn)去一個(gè)線程,并發(fā)量很低。

這里使用ThreadLocal去封裝SimpleDateFormat就解決了這個(gè)問題

package test;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test {
 static ThreadLocal<SimpleDateFormat> tl = new ThreadLocal<SimpleDateFormat>();

 public static class ParseDate implements Runnable {
 int i = 0;

 public ParseDate(int i) {
 this.i = i;
 }

 public void run() {
 try {
 if (tl.get() == null) {
  tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
 }
 Date t = tl.get().parse("2016-02-16 17:00:" + i % 60);
 System.out.println(i + ":" + t);
 } catch (ParseException e) {
 e.printStackTrace();
 }
 }
 }

 public static void main(String[] args) {
 ExecutorService es = Executors.newFixedThreadPool(10);
 for (int i = 0; i < 1000; i++) {
 es.execute(new ParseDate(i));
 }
 }

}

每個(gè)線程在運(yùn)行時(shí),會(huì)判斷是否當(dāng)前線程有SimpleDateFormat對(duì)象

if (tl.get() == null)

如果沒有的話,就new個(gè) SimpleDateFormat與當(dāng)前線程綁定

tl.set(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

然后用當(dāng)前線程的 SimpleDateFormat去解析

tl.get().parse("2016-02-16 17:00:" + i % 60);

一開始的代碼中,只有一個(gè) SimpleDateFormat,使用了 ThreadLocal,為每一個(gè)線程都new了一個(gè) SimpleDateFormat。

需要注意的是,這里不要把公共的一個(gè)SimpleDateFormat設(shè)置給每一個(gè)ThreadLocal,這樣是沒用的。需要給每一個(gè)都new一個(gè)SimpleDateFormat。

在hibernate中,對(duì)ThreadLocal有典型的應(yīng)用。

下面來看一下ThreadLocal的源碼實(shí)現(xiàn)

首先Thread類中有一個(gè)成員變量:

ThreadLocal.ThreadLocalMap threadLocals = null;

而這個(gè)Map就是ThreadLocal的實(shí)現(xiàn)關(guān)鍵

public void set(T value) {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t);
  if (map != null)
   map.set(this, value);
  else
   createMap(t, value);
 }

根據(jù) ThreadLocal可以set和get相對(duì)應(yīng)的value。

這里的ThreadLocalMap實(shí)現(xiàn)和HashMap差不多,但是在hash沖突的處理上有區(qū)別。

ThreadLocalMap中發(fā)生hash沖突時(shí),不是像HashMap這樣用鏈表來解決沖突,而是是將索引++,放到下一個(gè)索引處來解決沖突。

相關(guān)文章

  • JWT登錄認(rèn)證Springboot詳解

    JWT登錄認(rèn)證Springboot詳解

    文章主要介紹了如何在Java項(xiàng)目中使用JWT進(jìn)行用戶認(rèn)證和授權(quán),通過定義一個(gè)常量,編寫JWT工具類來生成和解析token,登錄時(shí)在服務(wù)端生成token并返回給客戶端,客戶端使用攔截器攔截請(qǐng)求,驗(yàn)證token的有效性,從而實(shí)現(xiàn)權(quán)限控制,文章旨在分享個(gè)人經(jīng)驗(yàn),為開發(fā)者提供參考
    2024-11-11
  • Java并發(fā)編程示例(四):可控的線程中斷

    Java并發(fā)編程示例(四):可控的線程中斷

    這篇文章主要介紹了Java并發(fā)編程示例(四):可控的線程中斷,在本節(jié),我們將使用一個(gè)線程查找指定目錄及其子目錄下文件來演示通過使用InterruptedException異常控制線程中斷,需要的朋友可以參考下
    2014-12-12
  • 詳解Java中Method的Invoke方法

    詳解Java中Method的Invoke方法

    這篇文章主要介紹了詳解Java中Method的Invoke方法,需要的朋友可以參考下
    2017-10-10
  • 盤點(diǎn)Java中延時(shí)任務(wù)的多種實(shí)現(xiàn)方式

    盤點(diǎn)Java中延時(shí)任務(wù)的多種實(shí)現(xiàn)方式

    當(dāng)需要一個(gè)定時(shí)發(fā)布系統(tǒng)通告的功能,如何實(shí)現(xiàn)??當(dāng)支付超時(shí),訂單自動(dòng)取消,如何實(shí)現(xiàn)?其實(shí)這些問題本質(zhì)都是延時(shí)任務(wù)的實(shí)現(xiàn),本文為大家盤點(diǎn)了多種常見的延時(shí)任務(wù)實(shí)現(xiàn)方法,希望對(duì)大家有所幫助
    2022-12-12
  • Spring加載加密的配置文件詳解

    Spring加載加密的配置文件詳解

    這篇文章主要為大家詳細(xì)介紹了Spring加載加密的配置文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • httpclient connect連接請(qǐng)求方法源碼解讀

    httpclient connect連接請(qǐng)求方法源碼解讀

    這篇文章主要為大家介紹了httpclient connect連接請(qǐng)求方法解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • Java中的泛型詳細(xì)解析

    Java中的泛型詳細(xì)解析

    這篇文章主要介紹了Java中的泛型詳細(xì)解析,泛型又稱參數(shù)化類型,是JDK5.0出現(xiàn)的新特性,解決了數(shù)據(jù)類型的安全型問題,Java泛型可以保證如果程序在編譯時(shí)沒用發(fā)出警告,運(yùn)行時(shí)就不會(huì)產(chǎn)生classCastException異常,需要的朋友可以參考下
    2024-01-01
  • SpringBoot Actuator埋點(diǎn)和監(jiān)控及簡(jiǎn)單使用

    SpringBoot Actuator埋點(diǎn)和監(jiān)控及簡(jiǎn)單使用

    最近做的項(xiàng)目涉及到埋點(diǎn)監(jiān)控、報(bào)表、日志分析的相關(guān)知識(shí),于是搗鼓的一番,下面把涉及的知識(shí)點(diǎn)及SpringBoot Actuator埋點(diǎn)和監(jiān)控的簡(jiǎn)單用法,給大家分享下,感興趣的朋友一起看看吧
    2021-11-11
  • MyBatis實(shí)現(xiàn)模糊查詢的幾種方式

    MyBatis實(shí)現(xiàn)模糊查詢的幾種方式

    這篇文章主要介紹了MyBatis實(shí)現(xiàn)模糊查詢的幾種方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • 手把手寫Spring框架

    手把手寫Spring框架

    Spring是于2003 年興起的一個(gè)輕量級(jí)的Java 開發(fā)框架,由Rod Johnson創(chuàng)建。簡(jiǎn)單來說,Spring是一個(gè)分層的JavaSE/EE full-stack(一站式) 輕量級(jí)開源框架
    2021-08-08

最新評(píng)論

潍坊市| 鱼台县| 克东县| 林芝县| 阿拉尔市| 哈尔滨市| 门源| 元朗区| 罗平县| 隆昌县| 满洲里市| 望奎县| 永平县| 玉山县| 长治市| 武城县| 稻城县| 四子王旗| 南乐县| 青海省| 开原市| 虎林市| 屏南县| 涡阳县| 东方市| 永胜县| 同心县| 江安县| 安岳县| 广平县| 同仁县| 东莞市| 北票市| 台东市| 永福县| 赤峰市| 万山特区| 环江| 嘉祥县| 任丘市| 新安县|