Exclusive or shared, what kind of lock you choose?


 

Before article has been written and fair locks unfair locked, the next step was introduced a second lock, he is the shared locks and exclusive locks, by definition, exclusive, can only be held by a thread, and shared, that can be shared by multiple threads.

Lock Category

1. Lock fair / unfair Lock

2. 3. reentrant lock exclusive lock / shared lock 4. mutex / write lock 5. optimistic locking / pessimistic lock segment 6. Lock 7. biased locking / lock lightweight / heavyweight lock 8. spinlocks

For the first time to share before we have said lock fair and unfair locked, this time we look at the group you want to resolve this exclusive locks and shared locks.

Exclusive lock

In fact, there are many exclusive lock name, it was called exclusive lock, exclusive lock was also called, in fact, generally is a meaning,

Exclusive lock, only one thread can be held,

And before we lock of fair and unfair lock his example also said it once, we can look at this example,

 

 

ReentrantLock (exclusive)

ReentrantLock is based AQS to achieve, AQS what is it?

AQS stands AbstractQueuedSynchronizer, if using translation software point of view "summary line synchronizer," but a lot of people like to call it abstract queue synchronizer. What in fact did not make so important, just remember English, this is the most important.

 

AQS It defines a set of multi-threaded frame synchronizer access to shared resources, many classes are dependent on ReentrantLock AQS for example, we would like to introduce.

You see the source code

/*    查询是否有任何线程正在等待与此锁相关联的给定条件。    请注意,由于超时和*中断可能随时发生,    此方法主要用于监视系统状态*/ public boolean hasWaiters(Condition condition) {    if (condition == null)        throw new NullPointerException();    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))        throw new IllegalArgumentException("not owner");    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);}

Here it indicates we are talking ReentrantLock is dependent AQS, and AQS it is a component of a core of JUC and contracting in. It is an indispensable component.

 

AQS sub ah solve problems, numerous details when synchronization is directed, for example acquiring synchronization state, the FIFO synchronous queue. To build synchronizer can bring many benefits based AQS. It not only can greatly reduce the implementation effort and do not have to deal with competition problems that occur at multiple locations.

 

AQS constructed based synchronizer, blocking occurs only at a time, thereby reducing the overhead of context switching, the throughput is improved.

 

The main use is AQS inheritance, the subclass inherits the synchronizer and implemented by its abstract methods to manage the synchronized state.

You can look at

public abstract class AbstractQueuedSynchronizer    extends AbstractOwnableSynchronizer    implements java.io.Serializable {    private static final long serialVersionUID = 7373984972572414691L;

And its typical example in ReentrantLock:

Using a state variable of type int member to represent the synchronization state, indicating when the lock has been acquired state> 0

This is what we see before the int c = getState ();

When c is equal to 0 when the thread owns the lock is not currently described, which provides three methods (getState (), setState (int newState), compareAndSetState (int expect, int update)) to operate the State synchronization state, so AQS to ensure the operation of the state is safe.

About AQS I would explain so much to be studied carefully what if you want to understand, and the source code in this ReentrantLock is such a

/**它默认是非公平锁*/public ReentrantLock() {    sync = new NonfairSync();}   /**   创建ReentrantLock,公平锁or非公平锁   */public ReentrantLock(boolean fair) {     sync = fair ? new FairSync() : new NonfairSync(); } /** 而他会分别调用lock方法和unlock方法来释放锁 */ public void lock() {         sync.lock();     } public void unlock() {         sync.release(1);     }

But in fact, he just calls lock and unlock methods, because we can not thread a little problem no, if entered into the waiting state, at this time if there is no unpark () method, there is no way to wake him, so, also comes on the heels appeared tryLock (), tryLock (long, TimeUnit) to do something to try to lock or supermarket to meet the needs of a certain scene.

 

ReentrantLock method-body will ensure that only one thread executing this code at the same time, or that the same time only one thread lock method will return. The remaining thread is suspended until acquiring the lock.

 

From here we can see, in fact, an exclusive lock function ReentrantLock realized: there is only one thread to acquire the lock, all the rest of the thread is suspended until the thread that holds the lock releases the lock, suspended thread wakes up restart lock contention.

 

In the source code to get the exclusive lock by calling acquire method, this method is actually blocked by AQS,

/***以独占模式获取,忽略中断。通过至少调用tryAcquire实现成功返回。否则线程排队,可能重复阻塞和解除阻塞,调用tryAcquire直到成功。此方法可用于实现方法lock。*/ public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }

 

It attempts to obtain a lock through to tryAcquire (implemented by subclasses Sync), implement the steps of the method of this lock is the source of the above

 

And do not get to lock the call acquireQueued method of AQS:

final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return interrupted;                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);        }    }

This generally means that the current driving means node is head node, and returns only the exclusive

When and if the following judgment, he would go to be blocked, but also to judge whether or not to interrupt, if our node status is Node.SIGNAL, finished, the thread will execute parkAndCheckInterrupt method to know that there's a thread release this time it will be a unpark to cycle to acquire the lock. And this method LockSupport.park (this) to suspend the current thread WATING state, we need to carry unpark a method to wake him, that I said that release, waiting by such a FIFO mechanism LOCK operation is realized.

 

This above code just be locked, but did not release the lock, if we get a lock not to be released, so it is natural to a situation where deadlock!

It is necessary to carry out a release,

We take a look inside is how to release the lock

 public void unlock()              { sync.release(1); } public final boolean release(int arg) {         if (tryRelease(arg)) {             Node h = head;             if (h != null && h.waitStatus != 0)                 unparkSuccessor(h);             return true;         }         return false;     }

unlock method of indirect call AQS release (1) to complete release

tryRelease (int) method is a special determination, if the head will be set up incoming unparkSuccessor (Node) method and returns true, otherwise is false.

public final boolean release(int arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);            return true;        }        return false;    }

 

And he performed unparkSuccessor methods, it already means to really release the lock. This is actually a process of acquiring exclusive locks lock and release the lock! Interested parties can go to the source code comments translated it and see.

 

Shared lock

We can see that from the exclusive our previous exclusive lock is a shared lock is actually used in a similar state to lock tag, but do not want anyone JAVA has two states, so the difference there, they not the same when the lock state.

 

The basic process is the same, the main difference is that the condition judging lock acquisition, because it is shared lock, it allows multiple threads simultaneously acquired, the number of simultaneous synchronization state is greater than 1, if the synchronization status is non-zero, then thread can acquire the lock only when the synchronization status is 0, only that the number of shared locks have all been acquired, the remaining thread can wait.

 

The most typical is ReentrantReadWriteLock in a read lock, it read locks can be shared, but it really can only write locks are exclusive.

 

We look at his code embodied acquire the lock and release the lock.

    //获取锁指定离不开这个lock方法,    public void lock() {                sync.acquireShared(1);    }    //acquireShared()首先会通过tryAcquireShared()来尝试获取锁。    //如果说获取不到那么他就回去执行  doAcquireShared(arg);直到获取到锁才会返回    //你看方法名do是不是想到了do-while呢?    public final void acquireShared(int arg) {            if (tryAcquireShared(arg) < 0)                doAcquireShared(arg);    }    // tryAcquireShared()来尝试获取锁。    protected int tryAcquireShared(int arg) {            throw new UnsupportedOperationException();    }    //只有这个方法获取到锁了才会进行返回    private void doAcquireShared(int arg) {            final Node node = addWaiter(Node.SHARED);            boolean failed = true;            try {                boolean interrupted = false;                for (;;) {                    final Node p = node.predecessor();                    if (p == head) {                        int r = tryAcquireShared(arg);                        if (r >= 0) {                            setHeadAndPropagate(node, r);                            p.next = null; // help GC                            if (interrupted)                                selfInterrupt();                            failed = false;                            return;                        }                    }                    if (shouldParkAfterFailedAcquire(p, node) &&                        parkAndCheckInterrupt())                        interrupted = true;                }            } finally {                if (failed)                    cancelAcquire(node);            }        }     //上面的这些方法全部都是在AbstractQueuedSynchronizer中     //而他通过Sync来调用的acquireShared     //而Sync则是继承的AbstractQueuedSynchronizer     abstract static class Sync extends AbstractQueuedSynchronizer      而他调用的tryAcquireShared则是在ReentrantReadWriteLock中     protected final int tryAcquireShared(int unused) {                 Thread current = Thread.currentThread();                 //获取状态                 int c = getState();                 //如果说锁状态不是0 并且获取锁的线程不是current线程 返回-1                 if (exclusiveCount(c) != 0 &&                     getExclusiveOwnerThread() != current)                     return -1;                 //统计读锁的次数                 int r = sharedCount(c);                 //若无需等待,并且共享读锁共享次数小于MAX_COUNT,则会把锁的共享次数加一,                 //否则他会去执行fullTryAcquireShared                 if (!readerShouldBlock() &&                     r < MAX_COUNT &&                     compareAndSetState(c, c + SHARED_UNIT)) {                     if (r == 0) {                         firstReader = current;                         firstReaderHoldCount = 1;                     } else if (firstReader == current) {                         firstReaderHoldCount++;                     } else {                         HoldCounter rh = cachedHoldCounter;                         if (rh == null || rh.tid != getThreadId(current))                             cachedHoldCounter = rh = readHolds.get();                         else if (rh.count == 0)                             readHolds.set(rh);                         rh.count++;                     }                     return 1;                 }                 return fullTryAcquireShared(current);             }        /** fullTryAcquireShared()会根据是否需要阻塞等待        读取锁的共享计数是否超过限制”等等进行处理。        如果不需要阻塞等待,并且锁的共享计数没有超过限制,        则通过CAS尝试获取锁,并返回1。*/      final int fullTryAcquireShared(Thread current) {          /*           * This code is in part redundant with that in           * tryAcquireShared but is simpler overall by not           * complicating tryAcquireShared with interactions between           * retries and lazily reading hold counts.           */          HoldCounter rh = null;          for (;;) {              int c = getState();              if (exclusiveCount(c) != 0) {                  if (getExclusiveOwnerThread() != current)                      return -1;                  // else we hold the exclusive lock; blocking here                  // would cause deadlock.              } else if (readerShouldBlock()) {                  // Make sure we're not acquiring read lock reentrantly                  if (firstReader == current) {                      // assert firstReaderHoldCount > 0;                  } else {                      if (rh == null) {                          rh = cachedHoldCounter;                          if (rh == null || rh.tid != getThreadId(current)) {                              rh = readHolds.get();                              if (rh.count == 0)                                  readHolds.remove();                          }                      }                      if (rh.count == 0)                          return -1;                  }              }              if (sharedCount(c) == MAX_COUNT)                  throw new Error("Maximum lock count exceeded");              if (compareAndSetState(c, c + SHARED_UNIT)) {                  if (sharedCount(c) == 0) {                      firstReader = current;                      firstReaderHoldCount = 1;                  } else if (firstReader == current) {                      firstReaderHoldCount++;                  } else {                      if (rh == null)                          rh = cachedHoldCounter;                      if (rh == null || rh.tid != getThreadId(current))                          rh = readHolds.get();                      else if (rh.count == 0)                          readHolds.set(rh);                      rh.count++;                      cachedHoldCounter = rh; // cache for release                  }                  return 1;              }          }      }

 

A lock acquisition process more source code is shared locks

Then be sure to release the lock of the

unlock()

    public void unlock() {            sync.releaseShared(1);    }    //和获取锁的过程类似,他首先会通过tryReleaseShared()去尝试释放共享锁。尝试成功,则直接返回;尝试失败,    //则通过doReleaseShared()去释放共享锁。     public final boolean releaseShared(int arg) {        if (tryReleaseShared(arg)) {            doReleaseShared();            return true;        }        return false;    }    //是尝试释放共享锁第一步。    protected final boolean tryReleaseShared(int unused) {        Thread current = Thread.currentThread();        if (firstReader == current) {            // assert firstReaderHoldCount > 0;            if (firstReaderHoldCount == 1)                firstReader = null;            else                firstReaderHoldCount--;        } else {            HoldCounter rh = cachedHoldCounter;            if (rh == null || rh.tid != getThreadId(current))                rh = readHolds.get();            int count = rh.count;            if (count <= 1) {                readHolds.remove();                if (count <= 0)                    throw unmatchedUnlockException();            }            --rh.count;        }        for (;;) {            int c = getState();            int nextc = c - SHARED_UNIT;            if (compareAndSetState(c, nextc))                // Releasing the read lock has no effect on readers,                // but it may allow waiting writers to proceed if                // both read and write locks are now free.                return nextc == 0;        }    }    //持续执行释放共享锁    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;                    if (ws == Node.SIGNAL) {                        if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                            continue;            // loop to recheck cases                        unparkSuccessor(h);                    }                    else if (ws == 0 &&                             !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))                        continue;                // loop on failed CAS                }                if (h == head)                   // loop if head changed                    break;            }        }

The above code is shared and non-shared lock lock source. Note that when, in fact, a mess here, some of which are defined in the ReentrantReadWriteLock in, and some methods are defined in AbstractQueuedSynchorizer in, so particular attention to toggle back and forth looking at the code, do not mistake.

 

to sum up

Exclusive lock: Only one thread acquires the lock.

Shared lock: You can have multiple threads at the same time acquire the lock.

On exclusive locks and shared locks, you understand me?

 


 

Java technology geeks public number, is founded by a group of people who love Java technology development, focused on sharing of original, high-quality Java articles. If you feel that our article is not bad, please help appreciated, watching, forwarding support, encourage us to share a better article. No public concern, we can reply, "blog Park" No background in public, freely available knowledge of Java / information interview must-see.

 

 

Guess you like

Origin www.cnblogs.com/justdojava/p/11212114.html