摘要:獲取資源失敗,將該線程加入等待隊(duì)列尾部,標(biāo)記為獨(dú)占模式。如果有剩余資源則會(huì)喚醒下一個(gè)線程,且整個(gè)過(guò)程忽略中斷的影響。
AQS概念及定義
ASQ:AbstractQueuedSynchronizer
它維護(hù)了一個(gè)volatile int state(代表共享資源)和一個(gè)FIFO線程等待隊(duì)列(多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列,有個(gè)內(nèi)部類Node定義了節(jié)點(diǎn)。隊(duì)列由AQS的volatile成員變量head和tail組成一個(gè)雙向鏈表)
資源共享方式AQS定義兩種資源共享方式:Exclusive(獨(dú)占,只有一個(gè)線程能執(zhí)行,如ReentrantLock)和Share(共享,多個(gè)線程可同時(shí)執(zhí)行,如Semaphore/CountDownLatch)。
自定義同步器AQS是抽象類,使用了模板方法設(shè)計(jì)模式,已經(jīng)將流程定義好,且實(shí)現(xiàn)了對(duì)等待隊(duì)列的維護(hù),因此實(shí)現(xiàn)者只需要按需實(shí)現(xiàn)AQS預(yù)留的四個(gè)方法即可。
isHeldExclusively():該線程是否正在獨(dú)占資源。只有用到condition才需要去實(shí)現(xiàn)它。
tryAcquire(int):獨(dú)占方式。嘗試獲取資源,成功則返回true,失敗則返回false。
tryRelease(int):獨(dú)占方式。嘗試釋放資源,成功則返回true,失敗則返回false。
tryAcquireShared(int):共享方式。嘗試獲取資源。負(fù)數(shù)表示失敗;0表示成功,但沒有剩余可用資源;正數(shù)表示成功,且有剩余資源。
tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true,否則返回false。
一般來(lái)說(shuō),自定義同步器要么是獨(dú)占方法,要么是共享方式,他們也只需實(shí)現(xiàn)tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared中的一種即可。但AQS也支持自定義同步器同時(shí)實(shí)現(xiàn)獨(dú)占和共享兩種方式,如ReentrantReadWriteLock。
核心方法分析1.1 acquire(int)
方法定義此方法是獨(dú)占模式下線程獲取共享資源的頂層入口。如果獲取到資源,線程直接返回,否則進(jìn)入等待隊(duì)列,直到獲取到資源為止,且整個(gè)過(guò)程忽略中斷的影響。
方法源碼/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }函數(shù)流程
1. tryAcquire():嘗試獲取資源。 2. addWaiter(Node.EXCLUSIVE):獲取資源失敗,將該線程加入等待隊(duì)列尾部,標(biāo)記為獨(dú)占模式。 3. acquireQueued(Node,int):獲取該node指定數(shù)量的資源數(shù),會(huì)一直等待成功獲取才返回,返回值是在獲取期間是否中斷過(guò)源碼分析
1. tryAcquire()
/** * Attempts to acquire in exclusive mode. This method should query * if the state of the object permits it to be acquired in the * exclusive mode, and if so to acquire it. * *This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used * to implement method {@link Lock#tryLock()}. * *
The default * implementation throws {@link UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return {@code true} if successful. Upon success, this object has * been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
這是個(gè)抽象方法,用于給實(shí)現(xiàn)者自定義實(shí)現(xiàn),此方法嘗試去獲取獨(dú)占資源。如果獲取成功,則直接返回true,否則直接返回false。這也正是tryLock()的語(yǔ)義。
2. addWaiter(Node)
/** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { //新建Node Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure //快速嘗試一次,使用CAS將node放到隊(duì)尾,失敗調(diào)用enq Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //保證將Node放入隊(duì)尾 enq(node); return node; }
enq源碼
/** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node"s predecessor */ private Node enq(final Node node) { for (;;) { Node t = tail; //如果尾節(jié)點(diǎn)為空,說(shuō)明隊(duì)列還未進(jìn)行初始化 if (t == null) { // Must initialize //CAS設(shè)置頭結(jié)點(diǎn) if (compareAndSetHead(new Node())) //初始頭尾相同,從下一次循環(huán)開始嘗試加入新Node tail = head; } else { node.prev = t; //CAS將當(dāng)前節(jié)點(diǎn)設(shè)置為尾節(jié)點(diǎn) if (compareAndSetTail(t, node)) { //設(shè)置成功返回當(dāng)前節(jié)點(diǎn) t.next = node; return t; } } } }
3. acquireQueued(Node, int)
/** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { //標(biāo)志是否成功獲取資源 boolean failed = true; try { //是否被中斷 boolean interrupted = false; for (;;) { //獲取前驅(qū)Node final Node p = node.predecessor(); //如果自己是隊(duì)列中第二個(gè)節(jié)點(diǎn),那會(huì)進(jìn)行嘗試獲取,進(jìn)入這里判斷要么是一次,要么是被前驅(qū)節(jié)點(diǎn)給unPark喚醒了。 if (p == head && tryAcquire(arg)) { //成功獲取資源,設(shè)置自身為頭節(jié)點(diǎn),將原來(lái)的頭結(jié)點(diǎn)剝離隊(duì)列 setHead(node); p.next = null; // help GC failed = false; return interrupted; } //判斷是否需要被park,如果需要進(jìn)行park并檢測(cè)是否被中斷 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { //如果獲取資源失敗了將當(dāng)前node取消, if (failed) cancelAcquire(node); } }
shouldParkAfterFailedAcquire方法
/** * Checks and updates status for a node that failed to acquire. * Returns true if thread should block. This is the main signal * control in all acquire loops. Requires that pred == node.prev. * * @param pred node"s predecessor holding status * @param node the node * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; //如果前驅(qū)的狀態(tài)已經(jīng)是signal,代表前驅(qū)釋放是會(huì)通知喚醒你,那么此node可以安心被park if (ws == Node.SIGNAL) /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ //如果前驅(qū)已經(jīng)被取消,那么從當(dāng)前node一直往前找,直到有非取消的node,直接排在它的后面,此時(shí)不需要park,會(huì)出去再嘗試一次獲取資源。 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don"t park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ //前驅(qū)節(jié)點(diǎn)沒有被取消,那么告訴前驅(qū)節(jié)點(diǎn)釋放的時(shí)候通知自己 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
parkAndCheckInterrupt()
/** * Convenience method to park and then check if interrupted * * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { //讓該線程進(jìn)入wait狀態(tài) LockSupport.park(this); //返回期間是被中斷過(guò) return Thread.interrupted(); }
acquireQueued流程總結(jié)
檢查自己是否是老二,且是否能獲得資源,能獲得自己成為head節(jié)點(diǎn),否則進(jìn)入流程2。
2.找到“有效”(not canceled)的前驅(qū),并通知前驅(qū)釋放了要“通知”(watiStatus=signal)我,安心被park。
3。被前驅(qū)unpark,或interrrupt(),繼續(xù)流程1。
首先調(diào)用實(shí)現(xiàn)者實(shí)現(xiàn)的tryAcquire()去獲取資源,如果成功則直接返回。
如果失敗,則新建一個(gè)獨(dú)占模式的節(jié)點(diǎn)加到隊(duì)列尾部。
通知一個(gè)有效的前驅(qū)記得釋放時(shí)喚醒自己,在喚醒時(shí)自己再進(jìn)行不斷tryAcquire()直到獲取到資源,返回是否被中斷過(guò)。
如果等待過(guò)程中被中斷過(guò),則將將中斷補(bǔ)上,調(diào)用當(dāng)前線程的interrupt().
至此acquire流程完結(jié),
1.2 release(int)
方法定義此方法是獨(dú)占模式下線程釋放共享資源的頂層入口。它會(huì)釋放指定量的資源,如果徹底釋放了(即state=0),它會(huì)喚醒等待隊(duì)列里的其他線程來(lái)獲取資源。這也正是unlock()的語(yǔ)義。
方法源碼/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(int arg) { //調(diào)用實(shí)現(xiàn)者的嘗試解鎖方法,因?yàn)橐呀?jīng)獲得鎖,所以基本不會(huì)失敗 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) //喚醒下一個(gè)節(jié)點(diǎn) unparkSuccessor(h); return true; } return false; }
unparkSuccessor()
/** * Wakes up node"s successor, if one exists. * * @param node the node */ private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0)//設(shè)置當(dāng)前節(jié)點(diǎn)的狀態(tài)允許失敗,失敗了也沒關(guān)系。 compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ //找到下一個(gè)需要被喚醒的節(jié)點(diǎn) Node s = node.next; if (s == null || s.waitStatus > 0) {//如果是空或被取消 s = null; //從尾節(jié)點(diǎn)開始尋找,直到找到最前面的有效的節(jié)點(diǎn)。因?yàn)殒i已經(jīng)釋放,所以從尾節(jié)點(diǎn)開始找可以避免因?yàn)楦卟l(fā)下復(fù)雜的隊(duì)列動(dòng)態(tài)變化帶來(lái)的邏輯判斷, for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }release小結(jié)
首先調(diào)用實(shí)現(xiàn)者的tryRelease(),失敗則返回false
成功則找到下一個(gè)有效的節(jié)點(diǎn)并喚醒它。
注意實(shí)現(xiàn)者實(shí)現(xiàn)tryRelease應(yīng)該是當(dāng)state為0時(shí)才返回
1.3 acquireShared(int)
方法定義此方法是共享模式下線程獲取共享資源的頂層入口。如果獲取到資源,線程直接返回。如果有剩余資源則會(huì)喚醒下一個(gè)線程,否則進(jìn)入wait,且整個(gè)過(guò)程忽略中斷的影響。
方法源碼/** * Acquires in shared mode, ignoring interrupts. Implemented by * first invoking at least once {@link #tryAcquireShared}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquireShared} until success. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquireShared} but is otherwise uninterpreted * and can represent anything you like. */ public final void acquireShared(int arg) { //嘗試獲取指定數(shù)量資源 if (tryAcquireShared(arg) < 0) //獲取資源直到成功 doAcquireShared(arg); }
共享模式下的流程與獨(dú)占模式極為相似,首先根據(jù)tryAcquireShared(arg)嘗試是否能獲取到資源,能則直接返回,不能則會(huì)進(jìn)入隊(duì)列按入隊(duì)順序依次喚醒嘗試獲取。
tryAcquireShared(int)
/** * Attempts to acquire in shared mode. This method should query if * the state of the object permits it to be acquired in the shared * mode, and if so to acquire it. * *This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. * *
The default implementation throws {@link * UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return a negative value on failure; zero if acquisition in shared * mode succeeded but no subsequent shared-mode acquire can * succeed; and a positive value if acquisition in shared * mode succeeded and subsequent shared-mode acquires might * also succeed, in which case a subsequent waiting thread * must check availability. (Support for three different * return values enables this method to be used in contexts * where acquires only sometimes act exclusively.) Upon * success, this object has been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if shared mode is not supported */ protected int tryAcquireShared(int arg) { throw new UnsupportedOperationException(); }
這是AQS預(yù)留給實(shí)現(xiàn)者的方法,用于共享模式下嘗試獲取指定數(shù)量的資源,返回值<0代表獲取失敗,=0代表獲取成功且無(wú)剩余資源,>0代表還有剩余資源
doAcquireShared(int)方法用于共享模式獲取資源會(huì)直到獲取成功才返回
/** * Acquires in shared uninterruptible mode. * @param arg the acquire argument */ private void doAcquireShared(int arg) { //添加當(dāng)前線程的Node模式為共享模式至隊(duì)尾, final Node node = addWaiter(Node.SHARED); boolean failed = true; try { boolean interrupted = false; for (;;) { //獲取前驅(qū)節(jié)點(diǎn) final Node p = node.predecessor(); //如果自己是老二才有嘗試的資格 if (p == head) { //嘗試獲取指定數(shù)量資源 int r = tryAcquireShared(arg); if (r >= 0) { //如果成功獲取,將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn),如果有剩余資源喚醒下一有效節(jié)點(diǎn) setHeadAndPropagate(node, r); p.next = null; // help GC //如果有中斷,自己補(bǔ)償中斷 if (interrupted) selfInterrupt(); failed = false; return; } } //判斷是否需要被park,和park后檢查是否被中弄斷 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { //如果獲取失敗,取消當(dāng)前節(jié)點(diǎn) if (failed) cancelAcquire(node); } }
流程和獨(dú)占模式幾乎一模一樣,但是代碼的書寫缺有不同,不知原作者是咋想的。區(qū)別于獨(dú)占不同的有兩點(diǎn)
添加模式為SHARED1的Node。
在成功獲取到資源后,設(shè)置當(dāng)前節(jié)點(diǎn)為head節(jié)點(diǎn)時(shí),如果還有剩余資源的話,會(huì)喚醒下一個(gè)有效的節(jié)點(diǎn),如果資源數(shù)量不夠下一節(jié)點(diǎn),下一節(jié)點(diǎn)會(huì)一直等待,直到其它節(jié)點(diǎn)釋放,并不會(huì)讓步給后面的節(jié)點(diǎn),取決于FIFO的按順序出隊(duì)。
setHeadAndPropagate()看有剩余資源的時(shí)候如何喚醒下一節(jié)點(diǎn)
/** * Sets head of queue, and checks if successor may be waiting * in shared mode, if so propagating if either propagate > 0 or * PROPAGATE status was set. * * @param node the node * @param propagate the return value from a tryAcquireShared */ private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below //將當(dāng)前節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn) setHead(node); /* * Try to signal next queued node if: * Propagation was indicated by caller, * or was recorded (as h.waitStatus either before * or after setHead) by a previous operation * (note: this uses sign-check of waitStatus because * PROPAGATE status may transition to SIGNAL.) * and * The next node is waiting in shared mode, * or we don"t know, because it appears null * * The conservatism in both of these checks may cause * unnecessary wake-ups, but only when there are multiple * racing acquires/releases, so most need signals now or soon * anyway. */ //如果有剩余資源 if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; //當(dāng)下一個(gè)有效節(jié)點(diǎn)存在且是共享模式時(shí),會(huì)喚醒它 if (s == null || s.isShared()) doReleaseShared(); } }
doReleaseShared()喚醒下一共享模式節(jié)點(diǎn)
/** * Release action for shared mode -- signals successor and ensures * propagation. (Note: For exclusive mode, release just amounts * to calling unparkSuccessor of head if it needs signal.) */ private void doReleaseShared() { /* * Ensure that a release propagates, even if there are other * in-progress acquires/releases. This proceeds in the usual * way of trying to unparkSuccessor of head if it needs * signal. But if it does not, status is set to PROPAGATE to * ensure that upon release, propagation continues. * Additionally, we must loop in case a new node is added * while we are doing this. Also, unlike other uses of * unparkSuccessor, we need to know if CAS to reset status * fails, if so rechecking. */ for (;;) { Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; //如果頭結(jié)點(diǎn)狀態(tài)是“通知后繼” if (ws == Node.SIGNAL) { //將其狀態(tài)改為0,表示已通知 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases //喚醒后繼 unparkSuccessor(h); } //如果已通知后繼,則改為可傳播,在下次acquire中的shouldParkAfterFailedAcquire會(huì)將改為SIGNAL else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } //如果頭結(jié)點(diǎn)變了,再次循環(huán) if (h == head) // loop if head changed break; } }acquireShared小結(jié)
共享模式acquire與獨(dú)占模式技術(shù)相同,唯一的不同就是在于如果當(dāng)前節(jié)點(diǎn)獲取資源成功且有剩余則會(huì)喚醒下一節(jié)點(diǎn),資源可以為多個(gè)線程功能分配,而獨(dú)占模式則就是一個(gè)線程獨(dú)占。
1.4 releaseShared(int)
方法定義此方法是共享模式下線程釋放共享資源的頂層入口。如果釋放資源成功,直接返回。如果有剩余資源則會(huì)喚醒下一個(gè)線程,且整個(gè)過(guò)程忽略中斷的影響。
方法源碼/** * Releases in shared mode. Implemented by unblocking one or more * threads if {@link #tryReleaseShared} returns true. * * @param arg the release argument. This value is conveyed to * {@link #tryReleaseShared} but is otherwise uninterpreted * and can represent anything you like. * @return the value returned from {@link #tryReleaseShared} */ public final boolean releaseShared(int arg) { //嘗試共享模式獲取資源 if (tryReleaseShared(arg)) { //喚醒下一節(jié)點(diǎn) doReleaseShared(); return true; } return false; }AQS的源碼分析就到這里為止由于本人目前功力尚淺,對(duì)AQS的理解停留在代碼級(jí)別,下此會(huì)將應(yīng)用補(bǔ)上,如有不對(duì)和遺漏歡迎各位補(bǔ)充。
參考文章
Java并發(fā)之AQS詳解
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68444.html
為什么讀webpack源碼 因?yàn)榍岸丝蚣茈x不開webpack,天天都在用的東西啊,怎能不研究 讀源碼能學(xué)到很多做項(xiàng)目看書學(xué)不到的東西,比如說(shuō)架構(gòu),構(gòu)造函數(shù),es6很邊緣的用法,甚至給函數(shù)命名也會(huì)潛移默化的影響等 想寫源碼,不看源碼怎么行,雖然現(xiàn)在還不知道寫什么,就算不寫什么,看看別人寫的總可以吧 知道世界的廣闊,那么多插件,那么多軟件開發(fā)師,他們?cè)谧鍪裁矗瑯邮菍慾s的,怎么他們能這么偉大 好奇...
摘要:前言本文的目的是閱讀理解的源碼,作為集合中重要的一個(gè)角色,平時(shí)用到十分多的一個(gè)類,深入理解它,知其所以然很重要。 前言 本文的目的是閱讀理解HashMap的源碼,作為集合中重要的一個(gè)角色,平時(shí)用到十分多的一個(gè)類,深入理解它,知其所以然很重要。本文基于Jdk1.7,因?yàn)镴dk1.8改變了HashMap的數(shù)據(jù)結(jié)構(gòu),進(jìn)行了優(yōu)化,我們先從基礎(chǔ)閱讀,之后再閱讀理解Jdk1.8的內(nèi)容 HashMa...
摘要:大多的初學(xué)者都會(huì)使用中間件來(lái)處理異步請(qǐng)求,其理解簡(jiǎn)單使用方便具體使用可參考官方文檔。源碼的源碼非常簡(jiǎn)潔,出去空格一共只有行,這行中如果不算上則只有行。官方文檔中的一節(jié)講解的非常好,也確實(shí)幫我理解了中間件的工作原理,非常推薦閱讀。 總覺得文章也應(yīng)該是有生命力的,歡迎關(guān)注我的Github上的博客,這里的文章會(huì)依據(jù)我本人的見識(shí),逐步更新。 大多redux的初學(xué)者都會(huì)使用redux-thunk...
摘要:主要邏輯本質(zhì)上還是回調(diào)函數(shù)那一套。通過(guò)的判斷完成異步和同步的區(qū)分。 主要邏輯: 本質(zhì)上還是回調(diào)函數(shù)那一套。通過(guò)_subscribers的判斷完成異步和同步的區(qū)分。通過(guò) resolve,reject -> publish -> invokeCallback -> resolve,reject的遞歸和下一條then的parent是上一條的child來(lái)完成then鏈的流轉(zhuǎn) 同步情況...
摘要:進(jìn)入傳入地址出來(lái)一個(gè)復(fù)雜對(duì)象把掛載到對(duì)象上太復(fù)雜我們先看可以緩存輸入的文件系統(tǒng)輸入文件系統(tǒng)輸出文件系統(tǒng),掛載到對(duì)象傳入輸入文件,監(jiān)視文件系統(tǒng),掛載到對(duì)象添加事件流打開插件讀取目錄下文件對(duì)文件名進(jìn)行格式化異步讀取目錄下文件同步方法就 進(jìn)入webpack.js //傳入地址,new Compiler出來(lái)一個(gè)復(fù)雜對(duì)象 compiler = new Compiler(options.conte...
閱讀 1836·2021-11-24 09:39
閱讀 2304·2021-09-30 09:47
閱讀 4173·2021-09-22 15:57
閱讀 1893·2019-08-29 18:36
閱讀 3591·2019-08-29 12:21
閱讀 604·2019-08-29 12:17
閱讀 1277·2019-08-29 11:25
閱讀 738·2019-08-28 18:26