摘要:變量對所有的線程都是可見的,對變量所有的寫操作都能立即反應(yīng)到其他線程之中,即變量在各個線程中是一致的。因為該在釋放等待線程后可以重用,所有稱為循環(huán)的。
線程安全就是防止某個對象或者值在多個線程中被修改而導(dǎo)致的數(shù)據(jù)不一致問題,因此我們就需要通過同步機制保證在同一時刻只有一個線程能夠訪問到該對象或數(shù)據(jù),修改數(shù)據(jù)完畢之后,再將最新數(shù)據(jù)同步到主存中,使得其他線程都能夠得到這個最新數(shù)據(jù)。下面我們就來了解Java一些基本的同步機制。
volatile關(guān)鍵字Java提供了一種稍弱的同步機制即volatile變量,用來確保將變量的更新操作通知到其他線程。當(dāng)把變量聲明為volatile類型后,編譯器與運行時都會注意到這個變量是共享的。然而,在訪問volatile變量時不會執(zhí)行加鎖操作,因此也就不會使線程阻塞,因此volatile變量是一種比synchronized關(guān)鍵字更輕量級的同步機制。
volatile變量對所有的線程都是可見的,對volatile變量所有的寫操作都能立即反應(yīng)到其他線程之中,即volatile變量在各個線程中是一致的。
有一種情況需要注意:volatile的語義不能確保遞增(count++)的原子性,除非你能確保只有一個線程對變量執(zhí)行寫操作。
public class VolatileTest{ public static volatile int i; public static void increase(){ i++; } } 查看字節(jié)碼: javap -c -l VolatileTest.class public class VolatileTest { public static volatile int i; public VolatileTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 1: 0 public static void increase(); Code: 0: getstatic #2 // Field i:I, 把i的值取到了操作棧頂,volatile保證了i值此時是正確的. 3: iconst_1 4: iadd // increase,但其他線程此時可能已經(jīng)把i值加大了好多 5: putstatic #2 // Field i:I ,把這個已經(jīng)out of date的i值同步回主內(nèi)存中,i值被破壞了. 8: return LineNumberTable: line 6: 0 line 7: 8 }
內(nèi)置鎖-synchronized加鎖機制即可以確保原子性又可以確保可見性,而volatile變量只能確??梢娦?。
Java中最常用的同步機制就是synchronized關(guān)鍵字,它是一種基于語言的粗略鎖,能夠作用于對象、函數(shù)、Class。每個對象都只有一個鎖,誰能夠拿到這個鎖誰就得到了訪問權(quán)限。當(dāng)synchronized作用于函數(shù)時,實際上鎖的也是對象,鎖定的對象是該函數(shù)所在類的對象。而synchronized作用于Class時則鎖的是這個Class類,并非某個具體對象。
synchronized同步方法和同步塊public class SynchronizedDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub final Test test = new Test(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub test.syncMethod(Thread.currentThread()); } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub test.syncMethod(Thread.currentThread()); } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub test.asyncMethod(Thread.currentThread()); } }).start(); } } class Test { public synchronized void syncMethod(Thread thread) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void asyncMethod(Thread thread) { synchronized (this) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()+2); } } } } syncMethod和asyncMethod代碼塊都加鎖時結(jié)果: Thread-0 Thread-0 Thread-0 Thread-1 Thread-1 Thread-1 Thread-2 Thread-2 Thread-2 #多個線程不能同時訪問同一個對象中的synchronized鎖的方法或代碼塊 syncMethod加鎖和asyncMethod代碼塊不加鎖時結(jié)果: class Test { public synchronized void syncMethod(Thread thread) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void asyncMethod(Thread thread) { synchronized (this) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()); } } } } Thread-0 Thread-22 Thread-22 Thread-22 Thread-0 Thread-0 Thread-1 Thread-1 Thread-1 #其他線程可以訪問同一個對象的非同步方法或代碼塊 syncMethod不加鎖和asyncMethod代碼塊不加鎖時結(jié)果: class Test { public void syncMethod(Thread thread) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void asyncMethod(Thread thread) { for(int i = 0;i < 3;i++) { System.out.println(thread.getName()+2); } } } Thread-0 Thread-1 Thread-22 Thread-22 Thread-22 Thread-0 Thread-1 Thread-1 Thread-0
synchronized同步方法和同步塊鎖定的是引用對象,synchronized作用于引用對象是防止其他線程訪問同一個對象的synchronized代碼塊或方法,但可以訪問其他非同步代碼塊或方法。
synchronized同步Class對象和靜態(tài)方法public class SynchronizedDemo { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub final Test test = new Test(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Test.syncStaticMethod(Thread.currentThread()); } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Test.syncStaticMethod(Thread.currentThread()); } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub Test.asyncStaticMethod(Thread.currentThread()); } }).start(); } } class Test { public synchronized static void syncStaticMethod(Thread thread) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName()); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void asyncStaticMethod(Thread thread) { synchronized (Test.class) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName() + 22); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } syncStaticMethod和asyncStaticMethod代碼塊都加鎖的結(jié)果: Thread-0 Thread-0 Thread-0 Thread-222 Thread-222 Thread-222 Thread-1 Thread-1 Thread-1 ##多個線程不能同時訪問添加了synchronized鎖的代碼塊和方法。 syncStaticMethod加鎖和asyncStaticMethod代碼塊不加鎖的結(jié)果: class Test { public synchronized static void syncStaticMethod(Thread thread) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName()); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void asyncStaticMethod(Thread thread) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName() + 22); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } Thread-0 Thread-222 Thread-222 Thread-0 Thread-0 Thread-222 Thread-1 Thread-1 Thread-1 ##多個線程可以同時訪問非同步的代碼塊和方法 syncStaticMethod加鎖和asyncStaticMethod代碼塊都不加鎖的結(jié)果: class Test { public static void syncStaticMethod(Thread thread) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName()); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void asyncStaticMethod(Thread thread) { for (int i = 0; i < 3; i++) { System.out.println(thread.getName() + 22); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } Thread-0 Thread-1 Thread-222 Thread-1 Thread-0 Thread-222 Thread-1 Thread-0 Thread-222
synchronized同步Class對象和靜態(tài)方法鎖的是Class對象,它的作用是防止多個線程同時訪問添加了synchronized鎖的代碼塊和方法。
總結(jié)當(dāng)一個線程正在訪問一個對象的synchronized方法,那么其他線程不能訪問該對象的其他synchronized方法,因為一個對象只有一把鎖,當(dāng)一個線程獲取了該對象的鎖之后,其他線程無法獲取該對象的鎖,所有無法訪問該對象的其他synchronized方法。
當(dāng)一個線程正在訪問一個對象的synchronized方法,那么其他線程能訪問該對象的非synchronized方法。因為非synchronized方法不需要獲取該對象的鎖。
如果一個線程A需要訪問對象object1的synchronized方法fun1,另外一個線程B需要訪問對象object2的synchronized方法fun1,即使object1和object2是同一類型,也不會產(chǎn)生線程安全問題,因為他們訪問的是不同的對象,所以不存在互斥問題。
如果一個線程執(zhí)行一個對象的非static synchronized方法,另一個線程執(zhí)行這個對象所屬類的static synchronized方法,此時不會發(fā)生互斥現(xiàn)象,因為訪問static synchronized方法占用的是類鎖,而訪問非static synchronized方法占用的是對象鎖,所以不存在互斥現(xiàn)象。
需要注意的是:對于synchronized方法或者synchronized代碼塊,當(dāng)出現(xiàn)異常時,JVM會自動釋放當(dāng)前線程占用的鎖,因此不會由于異常導(dǎo)致出現(xiàn)死鎖現(xiàn)象。
顯示鎖-ReentrantLock與Condition ReentrantLock在JDk 5.0之前,協(xié)調(diào)共享對象的訪問時,只有synchronized和volatile。Java 6.0增加了一種新的機制:ReentrantLock。顯示鎖ReentrantLock和內(nèi)置鎖synchronized相比,實現(xiàn)了相同的語義,但是具有更高的靈活性。
內(nèi)置鎖synchronized的獲取和釋放都在同一個代碼塊中,而顯示鎖ReentrantLock則可以將鎖的獲得和釋放分開。同時顯示鎖可以提供輪訓(xùn)鎖和定時鎖,同時可以提供公平鎖或者非公平鎖。
ReentrantLock的基本操作如下:
函 數(shù) | 作 用 |
---|---|
lock() | 獲取鎖 |
tryLock() | 嘗試獲取鎖 |
tryLock(timeout,Timeunit unit) | 在指定時間內(nèi)嘗試獲取鎖 |
unLock() | 釋放鎖 |
newCondition | 獲取鎖的Condition |
使用ReentrantLock的一般是lock、tryLock與unLock成對出現(xiàn),需要注意的是,千萬不要忘記調(diào)用unLock來釋放鎖,否則會引發(fā)死鎖等問題。
ReentrantLock的常用形式如下所示:
Lock lock = new ReentrantLock(); public void run() { lock.lock(); try { //執(zhí)行任務(wù) } finally { lock.unlock(); } }
需要注意的是,lock必須在finally塊中釋放,否則,如果受保護的代碼塊拋出異常,鎖就有可能永遠得不到釋放。而使用synchronized同步,JVM將確保鎖會獲得自動釋放,這也是Lock沒有完全替代掉synchronized的原因。
當(dāng)JVM用synchronized管理鎖定請求和釋放行為時,JVM在生成線程轉(zhuǎn)儲時能夠包括鎖定信息,這些對調(diào)式有非常大的價值,因為它們能標(biāo)識死鎖和其他異常行為的來源。Lock類只是普通的類,JVM不知道具體哪個線程擁有Lock對象。
Condition在ReentrantLock類中有一個重要的函數(shù)newCondition(),該函數(shù)用于獲取lock上的一個條件,也就是說Condition是和Lock綁定的。Condition用于實現(xiàn)線程間的通信,它是為了解決Object.wait()、notify()、notifyAll()難以使用的問題。
Condition的基本操作如下所示:
方 法 | 作 用 |
---|---|
await() | 線程等待 |
await(int time,TimeUnit unit) | 線程等待特定的時間,超過時間則為超時 |
signal() | 隨機喚醒某個等待線程 |
signalAll() | 喚醒所有等待中的線程 |
下面通過ReentrantLock和Condition類實現(xiàn)一個簡單的阻塞隊列。如果調(diào)用take方法時集合中沒有數(shù)據(jù),那么調(diào)用線程阻塞;如果調(diào)用put方法時,集合數(shù)據(jù)已滿則調(diào)用線程阻塞。但是這兩個阻塞條件是不同的,分別為notFull和notEmpty。MyArrayBlockingQueue的實現(xiàn)代碼如下:
public class MyArrayBlockingQueue信號量-Semaphore{ // 數(shù)據(jù)數(shù)組 private final T[] items; // 鎖 private final Lock mLock = new ReentrantLock(); // 數(shù)組滿的條件 private Condition notFull = mLock.newCondition(); // 數(shù)組空的條件 private Condition notEmpty = mLock.newCondition(); // 頭部 private int head; // 尾部 private int tail; // 數(shù)據(jù)數(shù)量 private int count; public MyArrayBlockingQueue(int maxSize) { // TODO Auto-generated constructor stub items = (T[]) new Object[maxSize]; } public MyArrayBlockingQueue() { // TODO Auto-generated constructor stub this(10); } public void put(T t) { mLock.lock(); try { // 如果數(shù)據(jù)已滿,等待 while (count == getCapacity()) { System.out.println("數(shù)據(jù)已滿,請等待"); notFull.await(); } System.out.println("存入數(shù)據(jù)"); items[tail] = t; if (++tail == getCapacity()) { tail = 0; } ++count; // 喚醒等待數(shù)據(jù)的線程 notEmpty.signalAll(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { mLock.unlock(); } } public T take() { mLock.lock(); try { // 如果數(shù)組數(shù)據(jù)為空,則阻塞 while (count == 0) { System.out.println("還沒有數(shù)據(jù),等待"); notEmpty.await(); } System.out.println("取出數(shù)據(jù)"); T t = items[head]; items[head] = null; if (++head == getCapacity()) { head = 0; } --count; // 喚醒添加數(shù)據(jù)的線程 notFull.signalAll(); return t; } catch (InterruptedException e) { // TODO: handle exception } finally { mLock.unlock(); } return null; } public int getCapacity() { return items.length; } public int size() { mLock.lock(); try { return count; } finally { mLock.unlock(); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub final MyArrayBlockingQueue mQueue = new MyArrayBlockingQueue<>( 5); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { for(int i = 0;i < 3;i++) mQueue.put("just"); try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }).start(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (true) { mQueue.take(); } } }).start(); } } 結(jié)果打印 存入數(shù)據(jù) 存入數(shù)據(jù) 存入數(shù)據(jù) 取出數(shù)據(jù) 取出數(shù)據(jù) 取出數(shù)據(jù) 還沒有數(shù)據(jù),等待 存入數(shù)據(jù) 存入數(shù)據(jù) 存入數(shù)據(jù) 取出數(shù)據(jù) 取出數(shù)據(jù) 取出數(shù)據(jù) 還沒有數(shù)據(jù),等待
Semaphore是一個計數(shù)信號量,它的本質(zhì)是一個“共享鎖”。信號量維護一個信號許可集合,線程可以通過調(diào)用acquire()來獲取信號量的許可。當(dāng)信號量有可用的許可時,線程能獲取該許可;否則線程必須等到,直到有可用的許可為止。線程可以通過release()來釋放它所持有的信號量許可。
Semaphore實現(xiàn)的功能類似食堂窗口。例如,食堂只有3個銷售窗口,要吃飯的有5個人,那么同時只有3個人買飯菜,每個人占用一個窗口,另外2人只能等待。當(dāng)前3個人有人離開之后,后續(xù)的人才可以占用窗口進行購買。這里的窗口就是我們所說的許可集,這里為3.一個人占用窗口時相當(dāng)于他調(diào)用acquire()獲取了許可,當(dāng)他離開時也就等于調(diào)用release()釋放了許可,這樣后續(xù)的人才可以得到許可。下面看看具體的示例:
public class SemaphoreTest { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ExecutorService service = Executors.newFixedThreadPool(3); final Semaphore semaphore = new Semaphore(3); for(int i = 0;i < 5;i++) { service.submit(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { semaphore.acquire(); System.out.println("剩余許可: " + semaphore.availablePermits()); Thread.sleep(2000); semaphore.release(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } } } 結(jié)果打?。? 剩余許可: 0 剩余許可: 0 剩余許可: 0 剩余許可: 2 剩余許可: 1
上述結(jié)果中:前三行是立刻輸出的,后兩行是等待2秒之后才輸出。原因是,信號量的許可集是3個,而消費線程是5個。前3個線程獲取了許可之后,信號量的許可就為0。此時后面的線程再調(diào)用acquire()就會阻塞,直到前3個線程執(zhí)行完之后,釋放了許可(不需要同時釋放許可)后兩個線程才能獲取許可并且繼續(xù)執(zhí)行。
循環(huán)柵欄-CyclicBarrierCyclicBarrier是一個同步輔助類,允許一組線程互相等待,直到達到某個公共屏障點。因為該barrier在釋放等待線程后可以重用,所有稱為循環(huán)的barrier。
下面看看示例:
public class CyclicBarrierTest { private static final int SIZE = 5; private static CyclicBarrier mCyclicBarrier; /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub mCyclicBarrier = new CyclicBarrier(SIZE, new Runnable() { @Override public void run() { // TODO Auto-generated method stub System.out.println("--滿足條件執(zhí)行特定操作,參與者: "+ mCyclicBarrier.getParties()); } }); for(int i = 0;i < SIZE;i++) { new WorkerThread().start(); } } static class WorkerThread extends Thread { @Override public void run() { // TODO Auto-generated method stub try { System.out.println(Thread.currentThread().getName() + "等待CyclicBarrier"); //將mCyclicBarrier的參與者數(shù)量加1 mCyclicBarrier.await(); //mCyclicBarrier的參與者數(shù)量加5時,才繼續(xù)往后執(zhí)行 System.out.println(Thread.currentThread().getName()+"繼續(xù)執(zhí)行"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BrokenBarrierException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 結(jié)果打印: Thread-1等待CyclicBarrier Thread-0等待CyclicBarrier Thread-2等待CyclicBarrier Thread-3等待CyclicBarrier Thread-4等待CyclicBarrier --滿足條件執(zhí)行特定操作,參與者: 5 Thread-4繼續(xù)執(zhí)行 Thread-3繼續(xù)執(zhí)行 Thread-2繼續(xù)執(zhí)行 Thread-0繼續(xù)執(zhí)行 Thread-1繼續(xù)執(zhí)行
從結(jié)果可以看出,只有當(dāng)有5個線程調(diào)用了mCyclicBarrier.await()方法后,后續(xù)的任務(wù)才會繼續(xù)執(zhí)行。上述例子中的5個WorkThread就位之后首先會執(zhí)行一個Runnable,也就是CyclicBarrier構(gòu)造函數(shù)的第二個參數(shù),該參數(shù)也可以省略。執(zhí)行該Runnable之后才會繼續(xù)執(zhí)行下面的任務(wù)。CyclicBarrier實際上相當(dāng)于可以用于多個線程等待,直到某個條件被滿足后開始繼續(xù)執(zhí)行后續(xù)的任務(wù)。對于該示例來說,這里的條件也就是有指定個數(shù)的線程調(diào)用了mCyclicBarrier.await()方法。
閉鎖-CountDownLatchCountDownLatch是一個同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待,直到條件被滿足。
示例如下:
public class CountDownLatchTest { private static final int LATCH_SIZE = 5; /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub try { CountDownLatch countDownLatch = new CountDownLatch(LATCH_SIZE); for(int i = 0;i < LATCH_SIZE;i++) { new WorkerThread(countDownLatch).start(); } System.out.println("主線程等待"); countDownLatch.await(); System.out.println("主線程繼續(xù)執(zhí)行"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static class WorkerThread extends Thread { private CountDownLatch latch; public WorkerThread(CountDownLatch latch) { this.latch = latch; } @Override public void run() { // TODO Auto-generated method stub try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "執(zhí)行操作"); //將latch的數(shù)量減1 latch.countDown(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 結(jié)果打?。? 主線程等待 Thread-3執(zhí)行操作 Thread-1執(zhí)行操作 Thread-0執(zhí)行操作 Thread-4執(zhí)行操作 Thread-2執(zhí)行操作 主線程繼續(xù)執(zhí)行
5個WorkThread對象在執(zhí)行完操作之后會調(diào)用CountDownLatch的countDown()函數(shù),當(dāng)5個WorkThread全都調(diào)用了countDown()之后主線程就會被喚醒繼續(xù)執(zhí)行任務(wù)。
CountDownLatch與CyclicBarrier區(qū)別CountDownLatch的作用是允許1或者多個線程等待其他線程完成執(zhí)行,而CyclicBarrier則是允許N個線程相互等待。
CountDownLatch的計數(shù)器無法被重置,CyclicBarrier的計數(shù)器可以被重置后使用。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/66132.html
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個類所提供的隊列式...
摘要:在中一般來說通過來創(chuàng)建所需要的線程池,如高并發(fā)原理初探后端掘金閱前熱身為了更加形象的說明同步異步阻塞非阻塞,我們以小明去買奶茶為例。 AbstractQueuedSynchronizer 超詳細原理解析 - 后端 - 掘金今天我們來研究學(xué)習(xí)一下AbstractQueuedSynchronizer類的相關(guān)原理,java.util.concurrent包中很多類都依賴于這個類所提供的隊列式...
摘要:在之前的文章中學(xué)習(xí)了關(guān)鍵字,可以保證變量在線程間的可見性,但他不能真正的保證線程安全。線程執(zhí)行到指令時,將會嘗試獲取對象所對應(yīng)的的所有權(quán),即嘗試獲得對象的鎖。從可見性上來說,線程通過持有鎖的方式獲取變量的最新值。 在之前的文章中學(xué)習(xí)了volatile關(guān)鍵字,volatile可以保證變量在線程間的可見性,但他不能真正的保證線程安全。 /** * @author cenkailun *...
摘要:表示的是兩個,當(dāng)其中任意一個計算完并發(fā)編程之是線程安全并且高效的,在并發(fā)編程中經(jīng)??梢娝氖褂?,在開始分析它的高并發(fā)實現(xiàn)機制前,先講講廢話,看看它是如何被引入的。電商秒殺和搶購,是兩個比較典型的互聯(lián)網(wǎng)高并發(fā)場景。 干貨:深度剖析分布式搜索引擎設(shè)計 分布式,高可用,和機器學(xué)習(xí)一樣,最近幾年被提及得最多的名詞,聽名字多牛逼,來,我們一步一步來擊破前兩個名詞,今天我們首先來說說分布式。 探究...
閱讀 2212·2021-11-25 09:43
閱讀 1177·2021-11-23 09:51
閱讀 3511·2021-11-23 09:51
閱讀 3637·2021-11-22 09:34
閱讀 1573·2021-10-09 09:43
閱讀 2134·2019-08-30 15:53
閱讀 3171·2019-08-30 14:07
閱讀 580·2019-08-28 18:14