乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      java ReentrantLock 分析

       流曲頻陽 2017-05-31


      并發(fā)編程中經(jīng)常用到的莫非是這個ReentrantLock這個類,線程獲取鎖和釋放鎖。還有一個則是synchronized,常用來多線程控制獲取鎖機制。


      先寫一個簡單的例子。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      package com.multi.thread;
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      public class AQSDemo {
          public static void main(String[] args) {
              Lock lock = new ReentrantLock(true);
              MyThread t1 = new MyThread("t1", lock);
              MyThread t2 = new MyThread("t2", lock);
              MyThread t3 = new MyThread("t3", lock);
              t1.start();
              t2.start();
              t3.start();
          }
           
      }
      class MyThread extends Thread {
          private Lock lock;
          public MyThread(String name, Lock lock) {
              super(name);
              this.lock = lock;
          }
          @Override
          public void run() {
              lock.lock();
              try {
                  System.out.println(Thread.currentThread() + "  is running ");
                  try {
                      Thread.sleep(500);
                  catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              finally {
                  lock.unlock();
              }
          }
      }


      這是個簡單的用ReentrantLock的代碼。


      知識點理解:

      ReentranctLock:

      1) 可重入性:大致意思就是如果一個函數(shù)能被安全的重復執(zhí)行,那么這個函數(shù)是可重復的。聽起來很繞口。

      2)可重入鎖:一個線程可以重復的獲取它已經(jīng)擁有的鎖。


      特性:

      1)ReentrantLock可以在不同的方法中使用。

      2)支持公平鎖和非公平鎖概念

          static final class NonfairSync extends Sync;(非公平鎖)

          static final class FairSync extends Sync;(公平鎖)

      3)支持中斷鎖,收到中斷信號可以釋放其擁有的鎖。

      4)支持超時獲取鎖:tryLock方法是嘗試獲取鎖,支持獲取鎖的是帶上時間限制,等待一定時間就會返回。


      ReentrantLock就先簡單說一下AQS(AbstractQueuedSynchronizer)。java.util.concurrent包下很多類都是基于AQS作為基礎(chǔ)開發(fā)的,Condition,BlockingQueue以及線程池使用的worker都是基于起實現(xiàn)的,其實就是將負雜的繁瑣的并發(fā)過程封裝起來,以便其他的開發(fā)工具更容易的開發(fā)。其主要通過volatile和Unsafe類的原子操作,來實現(xiàn)阻塞和同步。


      AQS是一個抽象類,其他類主要通過重載其tryAcquire(int arg)來獲取鎖,和tryRelease來釋放鎖。

      AQS不在這里做分析,會有單獨的一篇文章來學習AQS。


      ReentrantLock類里面主要有三個類,Sync,NonfairSync,F(xiàn)airSync這三個類,NonfairSync與FairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類。


      Sync是ReentrantLock實現(xiàn)公平鎖和非公平鎖的主要實現(xiàn),默認情況下ReentrantLock是非公平鎖。

          Lock lock = new ReentrantLock(true);   :true則是公平鎖,false就是非公平鎖,什么都不傳也是非公平鎖默認的。


      非公平鎖:

          lock.lock();點進去代碼會進入到,ReentranctLock內(nèi)部類Sync。

          

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
          abstract static class Sync extends AbstractQueuedSynchronizer {
              private static final long serialVersionUID = -5179523762034025860L;
              /**
               * Performs {@link Lock#lock}. The main reason for subclassing
               * is to allow fast path for nonfair version.
               */
              abstract void lock();
               
              ......省略。
            }

      這個抽象類Sync的里有一個抽象方法,lock(),供給NonfairSync,F(xiàn)airSync這兩個實現(xiàn)類來實現(xiàn)的。這個是一個模板方法設計模式,具體的邏輯供給子類來實現(xiàn)。

      非公平鎖的lock的方法,雖然都可以自己看,但是還是粘貼出來,說一下。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
       static final class NonfairSync extends Sync {
              private static final long serialVersionUID = 7316153563782823691L;
              /**
               * Performs lock.  Try immediate barge, backing up to normal
               * acquire on failure.
               */
              final void lock() {
                  if (compareAndSetState(01))
                      setExclusiveOwnerThread(Thread.currentThread());
                  else
                      acquire(1);
              }
              ......省略
          }

      其實重點看這個compareAndSetState(0,1),這個其實一個原子操作,是cas操作來獲取線程的資源的。其代表的是如果原來的值是0就將其設為1,并且返回true。其實這段代碼就是設置private volatile int state;,這個狀態(tài)的。

      其實現(xiàn)原理就是通過Unsafe直接得到state的內(nèi)存地址然后直接操作內(nèi)存的。設置成功,就說明已經(jīng)獲取到了鎖,如果失敗的,則會進入:

      1
      2
      3
      4
      5
      public final void acquire(int arg) {
              if (!tryAcquire(arg) &&
                  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                  selfInterrupt();
          }

      這個方法里,這個過程是先去判斷鎖的狀態(tài)是否為可用,如果鎖已被持有,則再判斷持有鎖的線程是否未當前線程,如果是則將鎖的持有遞增,這也是java層實現(xiàn)可重入性的原理。如果再次失敗,則進入等待隊列。就是要進去等待隊列了AQS有一個內(nèi)部類,是Node就是用來存放獲取鎖的線程信息。


      AQS的線程阻塞隊列是一個雙向隊列,提供了FiFO的特性,Head節(jié)點表示頭部,tail表示尾部。

      1)節(jié)點node,維護一個volatile狀態(tài),維護一個prev指向向前一個隊列節(jié)點,根據(jù)前一個節(jié)點的狀態(tài)來判斷是否獲取鎖。

      2)當前線程釋放的時候,只需要修改自身的狀態(tài)即可,后續(xù)節(jié)點會觀察到這個volatile狀態(tài)而改變獲取鎖。volatile是放在內(nèi)存中的,共享的,所以前一個節(jié)點改變狀態(tài)后,后續(xù)節(jié)點會看到這個狀態(tài)信息。


      獲取鎖失敗后就會加入到隊列里,但是有一點,不公平鎖就是,每個新來的線程來獲取所得時候,不是直接放入到隊列尾部,而是也去cas修改state狀態(tài),看看是否獲取鎖成功。


      總結(jié)非公平鎖:

      首先會嘗試改變AQS的狀態(tài),改變成功了就獲取鎖,否則失敗后再次通過判斷當前的state的狀態(tài)是否為0,如果為0,就再次嘗試獲取鎖。如果state不為0,該鎖已經(jīng)被其他線程持有了,但是其它線程也可能也是自己啊,所以也要判斷一下是否是自己獲取線程,如果是則是獲取成功,且鎖的次數(shù)要加1,這是可重入鎖,不是則加入到node阻塞隊列里。加入到隊列后則在for循環(huán)中通過判斷當前線程狀態(tài)來決定是否喲啊阻塞。可以看出在加入隊列前及阻塞前多次嘗試去獲取鎖,而避免進入線程阻塞,這是因為阻塞、喚醒都需要cpu的調(diào)度,以及上下文切換,這是個重量級的操作,應盡量避免。


      公平鎖:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      FairSync類:
      final void lock() {
         //先去判斷鎖的狀態(tài),而不是直接去獲取
          acquire(1);
      }
      AQS類:
      public final void acquire(int arg) {
          if (!tryAcquire(arg) &&
              acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
              selfInterrupt();
      }
      FairSync類:
      protected final boolean tryAcquire(int acquires) {
          final Thread current = Thread.currentThread();
          int c = getState();
          if (c == 0) {
           
          //hasQueuedPredecessors判斷是否有前節(jié)點,如果有就不會嘗試去獲取鎖
              if (!hasQueuedPredecessors() &&
                  compareAndSetState(0, acquires)) {
                  setExclusiveOwnerThread(current);
                  return true;
              }
          }
          else if (current == getExclusiveOwnerThread()) {
              int nextc = c + acquires;
              if (nextc < 0)
                  throw new Error("Maximum lock count exceeded");
              setState(nextc);
              return true;
          }
          return false;
      }


      公平鎖,主要區(qū)別是:什么事都要有個先來后到,先來的有先。獲取鎖的時候是先看鎖是否可用并且是否有節(jié)點,就是是否有阻塞隊列。有的話,就是直接放入到隊列尾部,而不是獲取鎖。


      釋放鎖:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      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;
      }
      protected final boolean tryRelease(int releases) {
          int c = getState() - releases;
          if (Thread.currentThread() != getExclusiveOwnerThread())
              throw new IllegalMonitorStateException();
          boolean free = false;
          if (c == 0) {
              free = true;
              setExclusiveOwnerThread(null);
          }
          setState(c);
          return free;
      }


      釋放鎖是很簡單的,就是先去改變state這個狀態(tài)的值,改變后如果狀態(tài)為0,則說明釋放成功了,如果直接可重入了多次,也要釋放很多次的鎖。


      釋放過程:

      Head節(jié)點就是當前持有鎖的線程節(jié)點,當釋放鎖時,從頭結(jié)點的next來看,頭結(jié)點的下一個節(jié)點如果不為null,且waitStatus不大于0,則跳過判斷,否則從隊尾向前找到最前的一個waitStatus的節(jié)點,然后通過LockSupport.unpark(s.thread)喚醒該節(jié)點線程??梢钥闯鯮eentrantLock的非公平鎖只是在獲取鎖的時候是非公平的,如果進入到等待隊列后,在head節(jié)點的線程unlock()時,會按照進入的順序來得到喚醒,保證了隊列的FIFO的特性。


      參考文章:

      http:///2017/01/09/%E7%94%B1ReentrantLock%E5%88%86%E6%9E%90JUC%E7%9A%84%E6%A0%B8%E5%BF%83AQS/


      http://www.cnblogs.com/leesf456/p/5383609.html


      https://github.com/pzxwhc/MineKnowContainer/issues/16


      http://www./24006.html



      本文出自 “10093778” 博客,請務必保留此出處http://10103778.blog.51cto.com/10093778/1930644

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多