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

Java多線程中ReentrantLock與Condition詳解

 更新時(shí)間:2017年11月22日 08:52:08   作者:vernonzheng  
這篇文章主要介紹了Java多線程中ReentrantLock與Condition詳解,需要的朋友可以參考下

一、ReentrantLock類

1.1什么是reentrantlock

java.util.concurrent.lock中的Lock框架是鎖定的一個(gè)抽象,它允許把鎖定的實(shí)現(xiàn)作為Java類,而不是作為語(yǔ)言的特性來(lái)實(shí)現(xiàn)。這就為L(zhǎng)ock的多種實(shí)現(xiàn)留下了空間,各種實(shí)現(xiàn)可能有不同的調(diào)度算法、性能特性或者鎖定語(yǔ)義。ReentrantLock類實(shí)現(xiàn)了Lock,它擁有與synchronized相同的并發(fā)性和內(nèi)存語(yǔ)義,但是添加了類似鎖投票、定時(shí)鎖等候和可中斷鎖等候的一些特性。此外,它還提供了在激烈爭(zhēng)用情況下更佳的性能。(換句話說(shuō),當(dāng)許多線程都想訪問(wèn)共享資源時(shí),JVM可以花更少的時(shí)候來(lái)調(diào)度線程,把更多時(shí)間用在執(zhí)行線程上。)

reentrant鎖意味著什么呢?簡(jiǎn)單來(lái)說(shuō),它有一個(gè)與鎖相關(guān)的獲取計(jì)數(shù)器,如果擁有鎖的某個(gè)線程再次得到鎖,那么獲取計(jì)數(shù)器就加1,然后鎖需要被釋放兩次才能獲得真正釋放。這模仿了synchronized的語(yǔ)義;如果線程進(jìn)入由線程已經(jīng)擁有的監(jiān)控器保護(hù)的synchronized塊,就允許線程繼續(xù)進(jìn)行,當(dāng)線程退出第二個(gè)(或者后續(xù))synchronized塊的時(shí)候,不釋放鎖,只有線程退出它進(jìn)入的監(jiān)控器保護(hù)的第一個(gè)synchronized塊時(shí),才釋放鎖。

1.2ReentrantLock與synchronized的比較

相同:ReentrantLock提供了synchronized類似的功能和內(nèi)存語(yǔ)義。

不同:

(1)ReentrantLock功能性方面更全面,比如時(shí)間鎖等候,可中斷鎖等候,鎖投票等,因此更有擴(kuò)展性。在多個(gè)條件變量和高度競(jìng)爭(zhēng)鎖的地方,用ReentrantLock更合適,ReentrantLock還提供了Condition,對(duì)線程的等待和喚醒等操作更加靈活,一個(gè)ReentrantLock可以有多個(gè)Condition實(shí)例,所以更有擴(kuò)展性。

(2)ReentrantLock的性能比synchronized會(huì)好點(diǎn)。

(3)ReentrantLock提供了可輪詢的鎖請(qǐng)求,他可以嘗試的去取得鎖,如果取得成功則繼續(xù)處理,取得不成功,可以等下次運(yùn)行的時(shí)候處理,所以不容易產(chǎn)生死鎖,而synchronized則一旦進(jìn)入鎖請(qǐng)求要么成功,要么一直阻塞,所以更容易產(chǎn)生死鎖。

1.3ReentrantLock擴(kuò)展的功能

1.3.1實(shí)現(xiàn)可輪詢的鎖請(qǐng)求

在內(nèi)部鎖中,死鎖是致命的——唯一的恢復(fù)方法是重新啟動(dòng)程序,唯一的預(yù)防方法是在構(gòu)建程序時(shí)不要出錯(cuò)。而可輪詢的鎖獲取模式具有更完善的錯(cuò)誤恢復(fù)機(jī)制,可以規(guī)避死鎖的發(fā)生。

如果你不能獲得所有需要的鎖,那么使用可輪詢的獲取方式使你能夠重新拿到控制權(quán),它會(huì)釋放你已經(jīng)獲得的這些鎖,然后再重新嘗試??奢喸兊逆i獲取模式,由tryLock()方法實(shí)現(xiàn)。此方法僅在調(diào)用時(shí)鎖為空閑狀態(tài)才獲取該鎖。如果鎖可用,則獲取鎖,并立即返回值true。如果鎖不可用,則此方法將立即返回值false。此方法的典型使用語(yǔ)句如下:

Lock lock = ...; 
if (lock.tryLock()) { 
try { 
// manipulate protected state 
} finally { 
lock.unlock(); 
} 
} else { 
// perform alternative actions 
} 

1.3.2實(shí)現(xiàn)可定時(shí)的鎖請(qǐng)求

當(dāng)使用內(nèi)部鎖時(shí),一旦開(kāi)始請(qǐng)求,鎖就不能停止了,所以內(nèi)部鎖給實(shí)現(xiàn)具有時(shí)限的活動(dòng)帶來(lái)了風(fēng)險(xiǎn)。為了解決這一問(wèn)題,可以使用定時(shí)鎖。當(dāng)具有時(shí)限的活

動(dòng)調(diào)用了阻塞方法,定時(shí)鎖能夠在時(shí)間預(yù)算內(nèi)設(shè)定相應(yīng)的超時(shí)。如果活動(dòng)在期待的時(shí)間內(nèi)沒(méi)能獲得結(jié)果,定時(shí)鎖能使程序提前返回??啥〞r(shí)的鎖獲取模式,由tryLock(long,TimeUnit)方法實(shí)現(xiàn)。

1.3.3實(shí)現(xiàn)可中斷的鎖獲取請(qǐng)求

可中斷的鎖獲取操作允許在可取消的活動(dòng)中使用。lockInterruptibly()方法能夠使你獲得鎖的時(shí)候響應(yīng)中斷。

1.4ReentrantLock不好與需要注意的地方

(1)lock必須在finally塊中釋放。否則,如果受保護(hù)的代碼將拋出異常,鎖就有可能永遠(yuǎn)得不到釋放!這一點(diǎn)區(qū)別看起來(lái)可能沒(méi)什么,但是實(shí)際上,它極為重要。忘記在finally塊中釋放鎖,可能會(huì)在程序中留下一個(gè)定時(shí)炸彈,當(dāng)有一天炸彈爆炸時(shí),您要花費(fèi)很大力氣才有找到源頭在哪。而使用同步,JVM將確保鎖會(huì)獲得自動(dòng)釋放

(2)當(dāng)JVM用synchronized管理鎖定請(qǐng)求和釋放時(shí),JVM在生成線程轉(zhuǎn)儲(chǔ)時(shí)能夠包括鎖定信息。這些對(duì)調(diào)試非常有價(jià)值,因?yàn)樗鼈兡軜?biāo)識(shí)死鎖或者其他異常行為的來(lái)源。Lock類只是普通的類,JVM不知道具體哪個(gè)線程擁有Lock對(duì)象。

二、條件變量Condition

條件變量很大一個(gè)程度上是為了解決Object.wait/notify/notifyAll難以使用的問(wèn)題。

我們通過(guò)一個(gè)實(shí)際的例子來(lái)解釋Condition的用法:

我們要打印1到9這9個(gè)數(shù)字,由A線程先打印1,2,3,然后由B線程打印4,5,6,然后再由A線程打印7,8,9. 這道題有很多種解法,現(xiàn)在我們使用Condition來(lái)做這道題

package cn.outofmemory.locks;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class App {
	static class NumberWrapper {
		public int value = 1;
	}
	public static void main(String[] args) {
		//初始化可重入鎖
		final Lock lock = new ReentrantLock();
		//第一個(gè)條件當(dāng)屏幕上輸出到3
		final Condition reachThreeCondition = lock.newCondition();
		//第二個(gè)條件當(dāng)屏幕上輸出到6
		final Condition reachSixCondition = lock.newCondition();
		//NumberWrapper只是為了封裝一個(gè)數(shù)字,一邊可以將數(shù)字對(duì)象共享,并可以設(shè)置為final
		//注意這里不要用Integer, Integer 是不可變對(duì)象
		final NumberWrapper num = new NumberWrapper();
		//初始化A線程
		Thread threadA = new Thread(new Runnable() {
			@Override
						public void run() {
				//需要先獲得鎖
				lock.lock();
				try {
					System.out.println("threadA start write");
					//A線程先輸出前3個(gè)數(shù)
					while (num.value <= 3) {
						System.out.println(num.value);
						num.value++;
					}
					//輸出到3時(shí)要signal,告訴B線程可以開(kāi)始了
					reachThreeCondition.signal();
				}
				finally {
					lock.unlock();
				}
				lock.lock();
				try {
					//等待輸出6的條件
					reachSixCondition.await();
					System.out.println("threadA start write");
					//輸出剩余數(shù)字
					while (num.value <= 9) {
						System.out.println(num.value);
						num.value++;
					}
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
				finally {
					lock.unlock();
				}
			}
		}
		);
		Thread threadB = new Thread(new Runnable() {
			@Override
						public void run() {
				try {
					lock.lock();
					while (num.value <= 3) {
						//等待3輸出完畢的信號(hào)
						reachThreeCondition.await();
					}
				}
				catch (InterruptedException e) {
					e.printStackTrace();
				}
				finally {
					lock.unlock();
				}
				try {
					lock.lock();
					//已經(jīng)收到信號(hào),開(kāi)始輸出4,5,6
					System.out.println("threadB start write");
					while (num.value <= 6) {
						System.out.println(num.value);
						num.value++;
					}
					//4,5,6輸出完畢,告訴A線程6輸出完了
					reachSixCondition.signal();
				}
				finally {
					lock.unlock();
				}
			}
		}
		);
		//啟動(dòng)兩個(gè)線程
		threadB.start();
		threadA.start();
	}
}

上述代碼中有完整的注釋,請(qǐng)參考注釋,理解Condition的用法。

基本思路就是首先要A線程先寫(xiě)1,2,3,這時(shí)候B線程應(yīng)該等待reachThredCondition信號(hào),而當(dāng)A線程寫(xiě)完3之后就通過(guò)signal告訴B線程“我寫(xiě)到3了,該你了”,這時(shí)候A線程要等嗲reachSixCondition信號(hào),同時(shí)B線程得到通知,開(kāi)始寫(xiě)4,5,6,寫(xiě)完4,5,6之后B線程通知A線程reachSixCondition條件成立了,這時(shí)候A線程就開(kāi)始寫(xiě)剩下的7,8,9了。條件(也稱為條件隊(duì)列或條件變量)為線程提供了一個(gè)含義,以便在某個(gè)狀態(tài)條件現(xiàn)在可能為true的另一個(gè)線程通知它之前,一直掛起該線程(即讓其“等待”)。因?yàn)樵L問(wèn)此共享狀態(tài)信息發(fā)生在不同的線程中,所以它必須受保護(hù),因此要將某種形式的鎖與該條件相關(guān)聯(lián)。等待提供一個(gè)條件的主要屬性是:以原子方式釋放相關(guān)的鎖,并掛起當(dāng)前線程,就像Object.wait做的那樣。

上述API說(shuō)明表明條件變量需要與鎖綁定,而且多個(gè)Condition需要綁定到同一鎖上。前面的Lock中提到,獲取一個(gè)條件變量的方法是Lock.newCondition()。

voidawait()throwsInterruptedException; 
voidawaitUninterruptibly(); 
longawaitNanos(longnanosTimeout)throwsInterruptedException; 
booleanawait(longtime,TimeUnitunit)throwsInterruptedException; 
booleanawaitUntil(Datedeadline)throwsInterruptedException; 
voidsignal(); 
voidsignalAll();

以上是Condition接口定義的方法,await*對(duì)應(yīng)于Object.wait,signal對(duì)應(yīng)于Object.notify,signalAll對(duì)應(yīng)于Object.notifyAll。特別說(shuō)明的是Condition的接口改變名稱就是為了避免與Object中的wait/notify/notifyAll的語(yǔ)義和使用上混淆,因?yàn)镃ondition同樣有wait/notify/notifyAll方法。

每一個(gè)Lock可以有任意數(shù)據(jù)的Condition對(duì)象,Condition是與Lock綁定的,所以就有Lock的公平性特性:如果是公平鎖,線程為按照FIFO的順序從Condition.await中釋放,如果是非公平鎖,那么后續(xù)的鎖競(jìng)爭(zhēng)就不保證FIFO順序了。

一個(gè)使用Condition實(shí)現(xiàn)生產(chǎn)者消費(fèi)者的模型例子如下。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProductQueue<T> {
	private final T[] items;
	private final Lock lock = new ReentrantLock();
	private Condition notFull = lock.newCondition();
	private Condition notEmpty = lock.newCondition();
	// 
	private int head, tail, count;
	public ProductQueue(int maxSize) {
		items = (T[]) new Object[maxSize];
	}
	public ProductQueue() {
		this(10);
	}
	public void put(T t) throws InterruptedException {
		lock.lock();
		try {
			while (count == getCapacity()) {
				notFull.await();
			}
			items[tail] = t;
			if (++tail == getCapacity()) {
				tail = 0;
			}
			++count;
			notEmpty.signalAll();
		}
		finally {
			lock.unlock();
		}
	}
	public T take() throws InterruptedException {
		lock.lock();
		try {
			while (count == 0) {
				notEmpty.await();
			}
			T ret = items[head];
			items[head] = null;
			//GC 
			// 
			if (++head == getCapacity()) {
				head = 0;
			}
			--count;
			notFull.signalAll();
			return ret;
		}
		finally {
			lock.unlock();
		}
	}
	public int getCapacity() {
		return items.length;
	}
	public int size() {
		lock.lock();
		try {
			return count;
		}
		finally {
			lock.unlock();
		}
	}
}

在這個(gè)例子中消費(fèi)take()需要隊(duì)列不為空,如果為空就掛起(await()),直到收到notEmpty的信號(hào);生產(chǎn)put()需要隊(duì)列不滿,如果滿了就掛起(await()),直到收到notFull的信號(hào)。

可能有人會(huì)問(wèn)題,如果一個(gè)線程lock()對(duì)象后被掛起還沒(méi)有unlock,那么另外一個(gè)線程就拿不到鎖了(lock()操作會(huì)掛起),那么就無(wú)法通知(notify)前一個(gè)線程,這樣豈不是“死鎖”了?

2.1await*操作

上一節(jié)中說(shuō)過(guò)多次ReentrantLock是獨(dú)占鎖,一個(gè)線程拿到鎖后如果不釋放,那么另外一個(gè)線程肯定是拿不到鎖,所以在lock.lock()和lock.unlock()之間可能有一次釋放鎖的操作(同樣也必然還有一次獲取鎖的操作)。我們?cè)倩仡^看代碼,不管take()還是put(),在進(jìn)入lock.lock()后唯一可能釋放鎖的操作就是await()了。也就是說(shuō)await()操作實(shí)際上就是釋放鎖,然后掛起線程,一旦條件滿足就被喚醒,再次獲取鎖!

public final void await() throws InterruptedException { 
 if (Thread.interrupted()) 
  throw new InterruptedException(); 
 Node node = addConditionWaiter(); 
 int savedState = fullyRelease(node); 
 int interruptMode = 0; 
 while (!isOnSyncQueue(node)) { 
  LockSupport.park(this); 
  if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) 
   break; 
 } 
 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) 
  interruptMode = REINTERRUPT; 
 if (node.nextWaiter != null) 
  unlinkCancelledWaiters(); 
 if (interruptMode != 0) 
  reportInterruptAfterWait(interruptMode); 
} 

上面是await()的代碼片段。上一節(jié)中說(shuō)過(guò),AQS在獲取鎖的時(shí)候需要有一個(gè)CHL的FIFO隊(duì)列,所以對(duì)于一個(gè)Condition.await()而言,如果釋放了鎖,要想再一次獲取鎖那么就需要進(jìn)入隊(duì)列,等待被通知獲取鎖。完整的await()操作是安裝如下步驟進(jìn)行的:

將當(dāng)前線程加入Condition鎖隊(duì)列。特別說(shuō)明的是,這里不同于AQS的隊(duì)列,這里進(jìn)入的是Condition的FIFO隊(duì)列。后面會(huì)具體談到此結(jié)構(gòu)。進(jìn)行2。

釋放鎖。這里可以看到將鎖釋放了,否則別的線程就無(wú)法拿到鎖而發(fā)生死鎖。進(jìn)行3。

自旋(while)掛起,直到被喚醒或者超時(shí)或者CACELLED等。進(jìn)行4。

獲取鎖(acquireQueued)。并將自己從Condition的FIFO隊(duì)列中釋放,表明自己不再需要鎖(我已經(jīng)拿到鎖了)。

這里再回頭介紹Condition的數(shù)據(jù)結(jié)構(gòu)。我們知道一個(gè)Condition可以在多個(gè)地方被await*(),那么就需要一個(gè)FIFO的結(jié)構(gòu)將這些Condition串聯(lián)起來(lái),然后根據(jù)需要喚醒一個(gè)或者多個(gè)(通常是所有)。所以在Condition內(nèi)部就需要一個(gè)FIFO的隊(duì)列。

private transient Node firstWaiter; 
private transient Node lastWaiter; 

上面的兩個(gè)節(jié)點(diǎn)就是描述一個(gè)FIFO的隊(duì)列。我們?cè)俳Y(jié)合前面提到的節(jié)點(diǎn)(Node)數(shù)據(jù)結(jié)構(gòu)。我們就發(fā)現(xiàn)Node.nextWaiter就派上用場(chǎng)了!nextWaiter就是將一系列的Condition.await*串聯(lián)起來(lái)組成一個(gè)FIFO的隊(duì)列。

2.2signal/signalAll操作

await*()清楚了,現(xiàn)在再來(lái)看signal/signalAll就容易多了。按照signal/signalAll的需求,就是要將Condition.await*()中FIFO隊(duì)列中第一個(gè)Node喚醒(或者全部Node)喚醒。盡管所有Node可能都被喚醒,但是要知道的是仍然只有一個(gè)線程能夠拿到鎖,其它沒(méi)有拿到鎖的線程仍然需要自旋等待,就上上面提到的第4步(acquireQueued)。

private void doSignal(Node first) { 
  do { 
    if ( (firstWaiter = first.nextWaiter) == null) 
      lastWaiter = null; 
    first.nextWaiter = null; 
  } while (!transferForSignal(first) && 
       (first = firstWaiter) != null); 
} 
 
private void doSignalAll(Node first) { 
  lastWaiter = firstWaiter = null; 
  do { 
    Node next = first.nextWaiter; 
    first.nextWaiter = null; 
    transferForSignal(first); 
    first = next; 
  } while (first != null); 
} 

final boolean transferForSignal(Node node) { 
  if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) 
    return false; 
 
  Node p = enq(node); 
  int c = p.waitStatus; 
  if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL)) 
    LockSupport.unpark(node.thread); 
  return true; 
} 

上面就是喚醒一個(gè)await*()線程的過(guò)程,根據(jù)前面的小節(jié)介紹的,如果要unpark線程,并使線程拿到鎖,那么就需要線程節(jié)點(diǎn)進(jìn)入AQS的隊(duì)列。所以可以看到在LockSupport.unpark之前調(diào)用了enq(node)操作,將當(dāng)前節(jié)點(diǎn)加入到AQS隊(duì)列。

總結(jié)

以上就是本文關(guān)于Java多線程中ReentrantLock與Condition詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。有什么問(wèn)題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家的。

相關(guān)文章

  • 深入解析Spring?AI框架如何在Java應(yīng)用中實(shí)現(xiàn)智能化交互的關(guān)鍵

    深入解析Spring?AI框架如何在Java應(yīng)用中實(shí)現(xiàn)智能化交互的關(guān)鍵

    本文詳細(xì)介紹了SpringAI框架在Java應(yīng)用中的應(yīng)用,包括實(shí)體類映射、函數(shù)回調(diào)等核心功能的實(shí)現(xiàn),通過(guò)源碼分析,幫助開(kāi)發(fā)者更好地理解和使用這些高級(jí)特性,提升業(yè)務(wù)效率,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Springboot項(xiàng)目Maven依賴沖突的問(wèn)題解決

    Springboot項(xiàng)目Maven依賴沖突的問(wèn)題解決

    使用Spring Boot和Maven進(jìn)行項(xiàng)目開(kāi)發(fā)時(shí),依賴沖突是一個(gè)常見(jiàn)的問(wèn)題,本文就來(lái)介紹一下Springboot項(xiàng)目Maven依賴沖突的問(wèn)題解決,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-07-07
  • 深入分析JAVA Synchronized關(guān)鍵字

    深入分析JAVA Synchronized關(guān)鍵字

    這篇文章主要介紹了析JAVA Synchronized關(guān)鍵字的相關(guān)知識(shí),文中代碼非常詳細(xì),幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用

    JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用

    本文主要介紹了JAVA虛擬機(jī)中 -D, -X, -XX ,-server參數(shù)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼

    Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼

    這篇文章主要介紹了Java利用Redis實(shí)現(xiàn)高并發(fā)計(jì)數(shù)器的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 詳解解密Java中的類型轉(zhuǎn)換問(wèn)題

    詳解解密Java中的類型轉(zhuǎn)換問(wèn)題

    這篇文章主要介紹了Java中的類型轉(zhuǎn)換問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • SpringBoot多數(shù)據(jù)源解決方案:dynamic-datasource-spring-boot-starter

    SpringBoot多數(shù)據(jù)源解決方案:dynamic-datasource-spring-boot-starter

    本文主要介紹了SpringBoot多數(shù)據(jù)源解決方案,使用dynamic-datasource-spring-boot-starter實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • Java使用TCP實(shí)現(xiàn)在線聊天的示例代碼

    Java使用TCP實(shí)現(xiàn)在線聊天的示例代碼

    這篇文章主要介紹了Java使用TCP實(shí)現(xiàn)在線聊天的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Java優(yōu)化重復(fù)冗余代碼的8種方式總結(jié)

    Java優(yōu)化重復(fù)冗余代碼的8種方式總結(jié)

    日常開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到一些重復(fù)代碼,最近小編優(yōu)化了一些系統(tǒng)中的重復(fù)代碼,用了好幾種的方式,感覺(jué)挺有用的,所以本文給大家講講優(yōu)化重復(fù)代碼的幾種方式
    2023-08-08
  • Spring boot通過(guò)AOP防止API重復(fù)請(qǐng)求代碼實(shí)例

    Spring boot通過(guò)AOP防止API重復(fù)請(qǐng)求代碼實(shí)例

    這篇文章主要介紹了Spring boot通過(guò)AOP防止API重復(fù)請(qǐng)求代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評(píng)論

博爱县| 博野县| 雅安市| 浦北县| 云南省| 建宁县| 郸城县| 镇安县| 临汾市| 凉城县| 平潭县| 敖汉旗| 湾仔区| 温宿县| 吉首市| 丽水市| 右玉县| 乌鲁木齐县| 玉龙| 镇远县| 阿拉尔市| 康定县| 肥东县| 克拉玛依市| 湟源县| 罗源县| 勐海县| 田阳县| 根河市| 邵阳市| 禄劝| 来安县| 通海县| 白山市| 青神县| 永嘉县| 吴川市| 龙胜| 忻城县| 新和县| 永年县|