Talking about high concurrency (11) implementation of several spin locks (5) provides the lock and unlock implementations for time-limited bounded queue locks. This article provides the implementation of trylock.
Trylock is a little more complex than lock. It must handle timeout. Timeout may occur in the following situations:
1. The first step is to wait for the queue to time out when the node has not yet been obtained. Simply return false.
2. Step 2: Wait for the queue to get the node, but it has not joined the work queue, and the node status can be changed to free for use by subsequent threads. Then, return false.
3. Step 3: Set the prenode of the node to the previous node, change the node status to aborted, and then return. When the frontend node is found to be aborted, the subsequent node will spin the prenode of the frontend node and change the node status to free, used by other threads.
The prenode field of qnode is set only when timeout occurs in the work queue and used only once by subsequent nodes. In addition, this field is always set first before use, therefore, you do not need to explicitly clear the domain elsewhere.
Public Boolean trylock (long time, timeunit unit) throws interruptedexception {long starttime = system. currenttimemillis (); long duration = timeunit. milliseconds. convert (time, Unit); long expectedtime = starttime + duration; backoff = new backoff (min_backoff, max_backoff); qnode node = waitings [random. nextint (size)]; // Step 1: first obtain a node in the array and set its status to waiting. Otherwise, the getnode is spin: While (true) {While (node. st Ate. Get ()! = State. free) {// when the lock is released, the state is set to released, and the released is set to free by the subsequent thread. // If the node is already at the end of the team and is released, then, you can directly get the int [] currentstamp = new int [1]; qnode tailnode = tail. get (currentstamp); If (tailnode = node & (tailnode. state. get () = state. aborted | tailnode. state. get () = state. released) {qnode prenode = NULL; // if the last node is in the aborted state, point tail to its previous node if (tailnode. state. get () = state. aborted) {Prenode = tailnode. prenode;} If (tail. compareandset (tailnode, prenode, currentstamp [0], currentstamp [0] + 1) {node. state. set (state. waiting); break getnode ;}} if (node. state. compareandset (state. free, state. waiting) {break;} Try {backoff. backoff ();} catch (interruptedexception e) {return false;} If (timeout (expectedtime, system. currenttimemillis () {return false ;}// add the second step to the queue int [] currentstamp = new I NT [1]; qnode pretailnode = NULL; do {pretailnode = tail. get (currentstamp); // if it times out before it is added to the queue, set the node status to free and use if (timeout (expectedtime, system. currenttimemillis () {node. state. set (state. free); Return false ;}}// if it is not added to the queue, it will always spin while (! Tail. compareandset (pretailnode, node, currentstamp [0], currentstamp [0] + 1); // step 3 spin the previous node. If the previous node is null, it turns out to be the first node to join the queue if (pretailnode! = NULL) {// The status spin state s of the previous node = pretailnode. state. Get (); While (s! = State. released) {If (S = state. aborted) {qnode temp = pretailnode; pretailnode = pretailnode. prenode; // you can release this node temp. state. set (state. free);} If (timeout (expectedtime, system. currenttimemillis () {node. prenode = pretailnode; node. state. set (state. aborted); Return false;} s = pretailnode. state. get () ;}// set the status of the previous node to free, which can be used by other threads by pretailnode. state. set (state. free);} // point the mynode of the thread to the nodemynode to which the lock is obtained. set (node); Return true ;}
High concurrency (13) implementation of several spin locks (6)