Java. util. concurrent package source code reading 25 Fork/Join framework Fork and Work-Stealing (rewrite), java. util

Source: Internet
Author: User
Tags value store

Java. util. concurrent package source code reading 25 Fork/Join framework Fork and Work-Stealing (rewrite), java. util

When I wrote the previous two articles 23 and 24, I had a lot of details that I was not very clear about. This article reorganized the source code related to Fork and Work-Stealing.

First, let's look at some member variables defined by the thread pool:

About scanGuard:

volatile int scanGuard;private static final int SG_UNIT = 1 << 16;private static final int  SMASK      = 0xffff;

ScanGuard's low 16-bit value (0 to 15 digits) is always equal to the Npower of 2 minus 1, representing the Npower of 2 greater than the number of Worker threads minus 1. Therefore, SMASK is used every time a low 16-bit data is retrieved.

The 16th bits of scanGuard are a flag used as a lock for updating the worker thread array. When the BIT data is 1, the worker thread array is locked, and other threads cannot update the worker thread.

SG_UNIT is used to update the 16th-bit value.

 

Let's talk about the three variables related to the task queue:

 

// An array of storage tasks. The length is 2 n ForkJoinTask <?> [] Queue; // The subscript of the last element array + 1 // If the array is regarded as a queue, the position is the end of the queue (add element in FIFO) // if it is regarded as a stack, the position is the top of the stack (the LIFO takes away the element) // This value can only be used by the current thread, so there is no multithreading problem, so no volatileint queueTop; // array subscript of the first element // that is, the position of the queue header. When an element is removed from the queue, this value is added to 1 // other thread stealing tasks (FIFO) will update this variable, so volatilevolatile int queueBase is required;

 

The Design of task queue is consistent with that of Work-Stealing (LIFO and FIFO are supported ).

Below is the scan method source code parsing (supplemented with some details ):

Private boolean scan (ForkJoinWorkerThread w, int a) {int g = scanGuard; // parallelism indicates the number of concurrent threads, which is generally equal to the number of threads that the CPU can run simultaneously, // The default value is the value returned by the availableProcessors method of the Runtime class, indicating the number of processors. Therefore, parallelism is greater than 0. // A is the number of active Worker threads, which must be greater than or equal to 0. Therefore, the condition parallelism = 1-a indicates that parallelism is 1 and a is 0. // No Worker thread is executing the task. The value of blockedCount is 0, which means no threads are involved because join is blocked. // If both conditions are met, it means that no thread is running. // It means that tasks cannot be stored in the worker thread, so m = 0, that is, the task cannot be stolen. // The value returned by g & SMASK is a value ranging from 0 to 15 digits of scanGuard (a value equal to the Npower of 2 minus the value of 1) int m = (parallelism = 1-a & blockedCount = 0 )? 0: g & SMASK; ForkJoinWorkerThread [] ws = workers; if (ws = null | ws. length <= m) return false; // steal the task for (int r = w. seed, k = r, j =-(m + m); j <= m + m; ++ j) {ForkJoinTask <?> T; ForkJoinTask <?> [] Q; int B, I; // obtain a random worker thread from the thread queue ForkJoinWorkerThread v = ws [k & m]; // v! = Null indicates that the random index thread exists // queueBase is not equal to queueTop, indicating that the thread's task queue is not empty/v. if the queue is not null, the task queue has been initialized. // (q. length-1) is equal to the N power of 2 minus one, and phase B get a // array subscript within the array length range // This string is determined to confirm that a thread with a task is found to steal the task if (v! = Null & (B = v. queueBase )! = V. queueTop & (q = v. queue )! = Null & (I = (q. length-1) & B)> = 0) {// u is the Unsafe index for computing. It is used to perform the CAS Operation long u = (I <ASHIFT) + ABASE; // (t = q [I])! = Null is used to determine whether the array contains tasks at this position. // v. queueBase = B in order to confirm that no thread is taking the task away // set the array element to null in the CAS operation to indicate that the task is taken away if (t = q [I])! = Null & v. queueBase = B & UNSAFE. compareAndSwapObject (q, u, t, null) {// v. queueBase = B + 1 update the position of the queue header int d = (v. queueBase = B + 1)-v. queueTop; v. stealHint = w. poolIndex; // d is the length of the task queue after a task is stolen if (d! = 0) signalWork (); w.exe cTask (t);} r ^ = r <13; r ^ = r >>> 17; w. seed = r ^ (r <5); // false indicates that the scan task return false;} // when j <0, the Worker thread else if (j <0) is randomly selected) {// exclusive or shift, update k r ^ = r <13; r ^ = r >>> 17; k = r ^ = r <5 ;} // j> = after 0, the attempt thread else ++ k;} // if the task cannot be scanned, but scanGuard is updated, // indicates that a new Worker thread is added in if (scanGuard! = G) return false; else {// extract the task from the task queue of the thread pool to execute the task // logic is similar to ForkJoinTask in the preceding task queue of other threads <?> T; ForkJoinTask <?> [] Q; int B, I; if (B = queueBase )! = QueueTop & (q = submissionQueue )! = Null & (I = (q. length-1) & B)> = 0) {long u = (I <ASHIFT) + ABASE; if (t = q [I])! = Null & queueBase = B & UNSAFE. compareAndSwapObject (q, u, t, null) {queueBase = B + 1; w.exe cTask (t) ;}return false ;}return true ;}}

When a Worker thread comes up, it directly steals the tasks of other threads. Does the job not matter? Let's see execTask:

Final void execTask (ForkJoinTask <?> T) {currentSteal = t; for (;) {// first execute the stolen task if (t! = Null) t. doExec (); // execute all your tasks first, and then steal other threads to execute if (queueTop = queueBase) break; // locallyFifo is generally from the thread pool setting // true use the FIFO method to get the task execution from the queue // false use the LIFO method (stack method) take task t = locallyFifo? LocallyDeqTask (): popTask ();} // update the steal task count + + stealCount; currentSteal = null ;}

 

A tryAwaitWork method is also involved in the work method of the thread pool (see article 23rd). The following is the parsing of this method:

Private boolean tryAwaitWork (ForkJoinWorkerThread w, long c) {int v = w. eventCount; // 0-30 characters of the ctl value store the information of the waiting thread // (refer to the ctl explanation in the work method parsing in article 23rd) // The waiting thread is stored in the stack mode. Therefore, set the first waiting thread to the next of the current thread, and the current thread to the first w. nextWait = (int) c; // reduce the number of running threads by 1, so reduce the value of the 48-63-bit ACSS by 1 long nc = (long) (v & E_MASK) | (c-AC_UNIT) & (AC_MASK | TC_MASK); // The two conditions are equivalent to changes in ctl if (ctl! = C |! UNSAFE. compareAndSwapLong (this, ctlOffset, c, nc) {long d = ctl; // The first condition indicates that the first waiting thread has changed (0-30 digits of the ctl value) // The second condition indicates that the number of running threads is reduced. // if both conditions are met, true is returned, and the return (int) d is forcibly scanned! = (Int) c & (d-c) & AC_MASK)> = 0L;} // for (int SC = w. stealCount; SC! = 0;) {// accumulate stealCount long s = stealCount; // Add the stealCount of thread w to the stealCount of thread pool, then set stealCount of w // to 0 if (UNSAFE. compareAndSwapLong (this, stealCountOffset, s, s + SC) SC = w. stealCount = 0; // if the eventCount of the thread changes, update stealCount else if (w. eventCount! = V) return true;} // shutdown or if tryTerminate is not set to false, it indicates that the current thread is not in the closing state. // (int) c! = 0 indicates that a thread is waiting. // parallelism + (int) (nc> AC_SHIFT) indicates that the number of active threads is 0 // blockedCount = 0 indicates that the number of threads waiting for join is 0 // quiescerCount = 0 indicates that the number of threads in the Quiesce thread pool is 0 // will introduce if ((! Shutdown |! TryTerminate (false) & (int) c! = 0 & parallelism + (int) (nc> AC_SHIFT) = 0 & blockedCount = 0 & quiescerCount = 0) // if the preceding conditions are met, the current thread pool does not have any threads working (including running // tasks and join waits). In this case, this thread will wait for a while // and then shut down the thread if there is still no event. IdleAwaitWork (w, nc, c, v); for (boolean rescanned = false;) {if (w. eventCount! = V) return true; // try to remove the current thread from the waiting queue. // once removed, eventCount changes and returns if (! Rescanned) {int g = scanGuard, m = g & SMASK; ForkJoinWorkerThread [] ws = workers; if (ws! = Null & m <ws. length) {rescanned = true; for (int I = 0; I <= m; ++ I) {ForkJoinWorkerThread u = ws [I]; if (u! = Null) {if (u. queueBase! = U. queueTop &&! TryReleaseWaiter () rescanned = false; if (w. eventCount! = V) return true ;}}if (scanGuard! = G | (queueBase! = QueueTop &&! TryReleaseWaiter () rescanned = false; if (! Rescanned) // get out of control and reduce conflicting threads. yield (); else // clear the interrupt status Thread before the Park. interrupted ();} else {w. parked = true; if (w. eventCount! = V) {w. parked = false; return true;} LockSupport. park (this); rescanned = w. parked = false ;}}}

 

The section about Fork is fragmented, and we will continue to talk about the Join process later.

 




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.