用synchronized实现的就一定是非公平锁吗?ReentrantLock(true)得到的“公平锁”真的公平吗?

        synchronized在升级成重量锁之前有轻量级锁、偏向锁等,其中几种锁的转化、升级、触发、撤销等,有一套复杂的原理,后面我会出相关的专篇博客。今天先来打破粗略使用层面的印象,延续前面的博文,讲一下synchronized重量级锁的争用,释放,唤醒机制。对比ReentrantLock,看看他们设计的相同和不同之处。

        synchronized的源码在jdk8/jdk8/hotspot: logicon-default.png?t=N7T8http://hg.openjdk.java.net/jdk8/jdk8/hotspot/ 可以下载出来各种修改节点的版本源代码,但是太乱,也可以从 OpenJDK 下载

   找hotspot:

        synchronized(obj)实现的同步锁机制中,真正互斥的锁是monitor,在这个环境下翻译就是对象监视器,俗称管程,监视对象的状态和线程占用,争用,达到同步器的效果。

        而我们平时所用的传参obj,其实不是锁对象,而是被锁的对象,也叫参照对象。只不过很多人喜欢命名为lock——锁对象(轻量级锁只需要这个obj作为存储锁状态,持有线程信息的参照资源,而且互斥锁对象monitor因他而生,这么说也没毛病,不过这里还是不要混淆),每个obj对象都可以产生一个monitor锁对象,那么找到相应的cpp源码文件objectMonitor.cpp:

下面贴出源码:

ObjectMonitor() {
    _header       = NULL;    // 升级为重量级锁时,存储参照对象的带有hashcodede markword
    _count        = 0;       // 记录获取该锁对象的线程数
    _waiters      = 0,       // 等待中的线程数
    _recursions   = 0;       // 线程重入次数
    _object       = NULL;    // 存储该 monitor 监视的对象,就是参照对象
    _owner        = NULL;    // 指向拥有该 monitor 的线程
    _WaitSet      = NULL;    // 等待线程 双向循环链表_WaitSet 指向第一个节点
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;   // 多线程竞争锁时的单向链表
    FreeNext      = NULL ;
    _EntryList    = NULL ;   // _owner 从该双向循环链表中唤醒线程,
    _SpinFreq     = 0 ;      // 自旋频率,每次尝试自旋的次数
    _SpinClock    = 0 ;      // 自旋时钟
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0; // 前一个拥有此监视器的线程 ID
  }

        首先并不是所有的线程获取同步器的情况都会申请monitor,只有锁膨胀为重量级锁的时候才会创建monitor对象。

  • _owner当有线程占有该 monitor 时 owner 标记为该线程的 ID。当线程释放 monitor 时 owner 恢复为 NULL。owner 是一个临界资源 JVM 是通过 CAS 操作来保证其线程安全的。
  • _cxq:
    1. 竞争队列(Contention Queue)cxq 是虚拟的队列,是在于 cxq 是由 Node (将线程封装成ObjectWaiter对象node)及其 next 指针逻辑构成的,并不是队列数据结构(单向),在线程进入 cxq 时会通过 CAS 自旋获取锁,如果获取不到锁就会进入 cxq 队列。
    2. _cxq 是一个临界资源 ,同样JVM 也是通过 CAS 原子指令来修改_cxq 队列。jvm默认策略是(后面所有的机制都是以默认策略来说的):每一次加入新的 Node 会在 cxq 的队头进行,通过 CAS 改变第一个节点的指针指向新增的节点,CAS成功后设置 Node 的 next 节点指向之前的头结点,不成功一直循环执行CAS,和AQS的入队操作是一样的设计思想。获取元素也是从 cxq 队首获取元素。也就是每当有新来的节点入队,它的 next 指针总是指向之前队列的头节点,而_cxq 指针会指向该新入队的节点,所以是后进先出LIFO。
  • _EntryList: 待入队列,可以理解为银行,医院排队叫号的预备号队列,路口左转待转区,不同于cxq,EntryList队列里线程是最有资格被唤醒阻塞。它是FIFO先进先出,即:尾进头出。除了cxq队列里面的线程到EntryList,notify()唤醒,有时会将waitSet里面的线程直接放到EntryList.
  • _WaitSet: 等待队列因为调用 wait 方法而被阻塞的线程会被放在该队列中。一个锁参照对象只能有一个Monitor锁对象,所以也只有一个WaitSet。
  • recursions:线程重入次数。

执行 monitorenter 指令时 调用以下代码:

IRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* thread, BasicObjectLock* elem))
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
  if (PrintBiasedLockingStatistics) {
    Atomic::inc(BiasedLocking::slow_path_entry_count_addr());
  }
  Handle h_obj(thread, elem->obj());
  assert(Universe::heap()->is_in_reserved_or_null(h_obj()),"must be NULL or an object");
// 是否使用偏向锁  JVM 启动时设置的偏向锁-XX:-UseBiasedLocking=false/true
  if (UseBiasedLocking) {
    // Retry fast entry if bias is revoked to avoid unnecessary inflation
    ObjectSynchronizer::fast_enter(h_obj, elem->lock(), true, CHECK);
  } else {
      // 不设置偏向锁会进入轻量级锁的逻辑
    ObjectSynchronizer::slow_enter(h_obj, elem->lock(), CHECK);
  }
  assert(Universe::heap()->is_in_reserved_or_null(elem->obj()),
         "must be NULL or an object");
#ifdef ASSERT
  thread->last_frame().interpreter_frame_verify_monitor(elem);
#endif
IRT_END

slow_enter 方法主要是轻量级锁的一些操作,如果操作失败则会膨胀为重量级锁,enter 方法则为重量级锁的入口源码如下:

void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = THREAD ;
  void * cur ;
  // 省略部分代码
  /**
  通过 CAS 操作尝试把 monitor 的 _owner 字段设置为当前线程
  在ObjectMonitor::enter进入的时候会调用Actomic当中的cmpxchg_ptr;
  Atomic::cmpxchg_ptr(Self, &_owner, NULL)
  该函数属于linux内核系统中的函数,依赖cpu去做原子操作
  CAS是一个原子的赋值操作;
  作用就是将monitor对象当中的_owner设置成这个当前线程Self;
  */
  // 通过 CAS 操作尝试把 monitor 的_owner 字段设置为当前线程
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     assert (_recursions == 0   , "invariant") ;
     assert (_owner      == Self, "invariant") ;
     return ;
  }

 // 线程重入,recursions++ 如果当前线程的_owner时当前线程,锁计数+1,表明进入了新的拥有相同锁的同步代码块
  if (cur == Self) {
     _recursions ++ ;
     return ;
  }

/**
如果当前线程第一次来抢monitor该锁;
如果当前线程是第一次进入该monitor,如果抢到锁了;
设置_recursions为1,并且将_owner设置为当前线程;
最后返回即表示当前线程竞争到该锁;
*/
    // 如果当前线程是第一次进入该 monitor, 设置_recursions 为 1,_owner 为当前线程
  if (Self->is_lock_owned ((address)cur)) {
    assert (_recursions == 0, "internal state error");
    _recursions = 1 ;
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }
//以上操作都没有抢到锁,进入循环等待机会
    for (;;) {
      jt->set_suspend_equivalent();
        // 如果获取锁失败,则等待锁的释放;
      EnterI (THREAD) ;

      if (!ExitSuspendEquivalent(jt)) break ;
          _recursions = 0 ;
      _succ = NULL ;
      exit (false, Self) ;

      jt->java_suspend_self();
    }
    Self->set_current_pending_monitor(NULL);
  }
}

monitor等待

  1. 当前线程被封装成 ObjectWaiter 对象 node,状态设置成 ObjectWaiter::TS_CXQ。
  2. for 循环通过 CAS 把 node 节点 push 到_cxq列表中,同一时刻可能有多个线程把自己的 node 节点 push 到_cxq列表中。
  3. node 节点 push 到_cxq 列表之后,通过自旋尝试获取锁,如果还是没有获取到锁则通过 park 将当前线程挂起等待被唤醒。
  4. 当该线程被唤醒时会从挂起的点继续执行,通过 ObjectMonitor::TryLock 尝试获取锁。

// 省略部分代码
void ATTR ObjectMonitor::EnterI (TRAPS) {
    Thread * Self = THREAD ;
    assert (Self->is_Java_thread(), "invariant") ;
    assert (((JavaThread *) Self)->thread_state() == _thread_blocked   , "invariant") ;

    // Try lock 尝试获取锁
    if (TryLock (Self) > 0) {
        assert (_succ != Self              , "invariant") ;
        assert (_owner == Self             , "invariant") ;
        assert (_Responsible != Self       , "invariant") ;
        // 如果获取成功则退出,避免 park unpark 系统调度的开销
        return ;
    }

    // 自旋获取锁
    if (TrySpin(Self) > 0) {
        assert (_owner == Self, "invariant");
        assert (_succ != Self, "invariant");
        assert (_Responsible != Self, "invariant");
        return;
    }

    // 当前线程被封装成 ObjectWaiter 对象 node, 状态设置成 ObjectWaiter::TS_CXQ
    ObjectWaiter node(Self) ;
    Self->_ParkEvent->reset() ;
    node._prev   = (ObjectWaiter *) 0xBAD ;
    node.TState  = ObjectWaiter::TS_CXQ ;

    // 通过 CAS 把 node 节点 push 到_cxq 列表中
    ObjectWaiter * nxt ;
    //期间可能会有失败,循环将所有没抢到锁的线程都放入cxq中
    for (;;) {
        // node节点插在头部,置换为_cxq队列的头节点
        node._next = nxt = _cxq ;
        if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
         // Interference - the CAS failed because _cxq changed. Just retry.
        // As an optional optimization we retry the lock.
        //在放入cxq的期间尝试再次获取锁,基本和AQS逻辑一样的。AQS应该是就是仿照jvm写的
        if (TryLock (Self) > 0) {
            assert (_succ != Self         , "invariant") ;
            assert (_owner == Self        , "invariant") ;
            assert (_Responsible != Self  , "invariant") ;
            return ;
        }
    }

    for (;;) {
        // 本段代码的主要思想和 AQS 中相似可以类比来看
        // 线程在挂起前做一下挣扎,看能不能获取到锁
        if (TryLock (Self) > 0) break ;
        assert (_owner != Self, "invariant") ;

        if ((SyncFlags & 2) && _Responsible == NULL) {
           Atomic::cmpxchg_ptr (Self, &_Responsible, NULL) ;
        }

        // 满足条件则 park self
        if (_Responsible == Self || (SyncFlags & 1)) {
            TEVENT (Inflated enter - park TIMED) ;
            Self->_ParkEvent->park ((jlong) RecheckInterval) ;
            // Increase the RecheckInterval, but clamp the value.
            RecheckInterval *= 8 ;
            if (RecheckInterval > 1000) RecheckInterval = 1000 ;
        } else {
            TEVENT (Inflated enter - park UNTIMED) ;
            // 通过 park 将当前线程挂起,等待被唤醒
            Self->_ParkEvent->park() ;
        }

        if (TryLock(Self) > 0) break ;
        // 再次尝试自旋
        if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) > 0) break;
    }
    return ;
}

该线程被唤醒时,会从挂起的点继续执行,通过ObjectMonitor::TryLock尝试获取锁,TryLock方法实现如下:

int ObjectMonitor::TryLock(Thread * Self){
  for(;;){
    void * own = owner;
    if(own != null) return 0;
    if(Atomic::cmpxchg_ptr (Self, &owner, NULL) == NULL){
      // Either guarantee recursions == 0 or set _recursions = 0.
      assert (recursions == 0,    "invariant");
      assert (_owner      == Self, "invariant");
      // CONSIDER: set or assert that OwnerIsThread == 1
      return 1;
    }
    // The lock had been freen momentarily, but we lost the race to the lock.
    // Interference -- the CAS faild.
    // we can either return -1 or retry.
    // Retry doesn't make as much sense because the lock was just acquired.
    if(true) return -1;
  }
}

当某个持有锁的线程执行完同步代码块时,会释放锁并 unpark 后续线程(由于篇幅只保留重要代码)。

下面是openJdk中阻塞线程被唤醒阻塞的源代码:

void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS) {
   Thread * Self = THREAD ;
  
   if (_recursions != 0) {
     _recursions--;        // this is simple recursive enter
     TEVENT (Inflated exit - recursive) ;
     return ;
   }

      ObjectWaiter * w = NULL ;
      int QMode = Knob_QMode ;

    // 直接绕过 EntryList 队列,从 cxq 队列中获取线程用于竞争锁
      if (QMode == 2 && _cxq != NULL) {
          w = _cxq ;
          assert (w != NULL, "invariant") ;
          assert (w->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
    // cxq 队列插入 EntryList 尾部
      if (QMode == 3 && _cxq != NULL) {
          w = _cxq ;
          // _cxq指向null
          for (;;) {
             assert (w != NULL, "Invariant") ;
             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
             if (u == w) break ;
             w = u ;
          }
          ObjectWaiter * q = NULL ;
          ObjectWaiter * p ;
          // 单向链表转双向链表
          for (p = w ; p != NULL ; p = p->_next) {
              guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
              p->TState = ObjectWaiter::TS_ENTER ;
              p->_prev = q ;
              q = p ;
          }

          ObjectWaiter * Tail ;
          for (Tail = _EntryList ; Tail != NULL && Tail->_next != NULL ; Tail = Tail->_next) ;
          // 放到entryList列表尾部 
          //如果EntryList为空,直接全量放入到EntryList==赋值给EntryList
          if (Tail == NULL) {
              _EntryList = w ;
          } else {
              // EntryList不为空,直接放入尾部
              Tail->_next = w ;
              w->_prev = Tail ;
          }
      }

    // cxq 队列插入到_EntryList 头部
      if (QMode == 4 && _cxq != NULL) {
          // 把 cxq 队列放入 EntryList
          // 此策略确保最近运行的线程位于 EntryList 的头部
          w = _cxq ;
           // _cxq指向null
          for (;;) {
             assert (w != NULL, "Invariant") ;
             ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
             if (u == w) break ;
             w = u ;
          }
          assert (w != NULL              , "invariant") ;

          ObjectWaiter * q = NULL ;
          ObjectWaiter * p ;
           // 单向链表转双向链表
          for (p = w ; p != NULL ; p = p->_next) {
              guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
              p->TState = ObjectWaiter::TS_ENTER ;
              p->_prev = q ;
              q = p ;
          }
        // 放入头部:直接将节点的next指向EntryList
          if (_EntryList != NULL) {
              q->_next = _EntryList ;
              _EntryList->_prev = q ;
          }
          //同样 EntryList为空,直接将转化后的cxq赋值给EntryList==全量放入到EntryList
          _EntryList = w ;
      }

      w = _EntryList  ;
      if (w != NULL) {
          assert (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
      w = _cxq ;
      if (w == NULL) continue ;

    // 
      for (;;) {
          assert (w != NULL, "Invariant") ;
          ObjectWaiter * u = (ObjectWaiter *) Atomic::cmpxchg_ptr (NULL, &_cxq, w) ;
          if (u == w) break ;
          w = u ;
      }

      if (QMode == 1) {
         // QMode == 1 : 把 cxq 倾倒入 EntryList 逆序
         // 单向链表改双向,且逆序。s代表前一个节点,u代表下一个节点
         ObjectWaiter * s = NULL ;
         ObjectWaiter * t = w ;
         ObjectWaiter * u = NULL ;
         // 这里来做倒置
         while (t != NULL) {
             guarantee (t->TState == ObjectWaiter::TS_CXQ, "invariant") ;
             t->TState = ObjectWaiter::TS_ENTER ;
             u = t->_next ;
             t->_prev = u ;
             t->_next = s ;
             s = t;
             t = u ;
         }
         // 由上面判断,此时EntryList必为空
         _EntryList  = s ;
         assert (s != NULL, "invariant") ;
      } else {
         // QMode == 0 默认值,其实entryList== null 最终逻辑和 QMode == 2 QMode == 3        
         //是一样的
         // 先将cxq全量放入EntryList:直接赋值
         _EntryList = w ;
         ObjectWaiter * q = NULL ;
         ObjectWaiter * p ;
          // 将单向链表构造成双向环形链表;
         for (p = w ; p != NULL ; p = p->_next) {
             guarantee (p->TState == ObjectWaiter::TS_CXQ, "Invariant") ;
             p->TState = ObjectWaiter::TS_ENTER ;
             p->_prev = q ;
             q = p ;
         }
      }
    // 不为空,说明已经唤醒恢复成功
      if (_succ != NULL) continue;

      w = _EntryList  ;
      if (w != NULL) {
          guarantee (w->TState == ObjectWaiter::TS_ENTER, "invariant") ;
          ExitEpilog (Self, w) ;
          return ;
      }
   }
}

退出同步块的策略:

如果没有线程获取锁成功,需要唤醒队列中的节点

  • Qmode == 2 && CXQ!=null优先从CXQ队列中唤醒线程,从头部开始唤醒
  • Qmode == 3 && CXQ!=null将CXQ队列尾插法插入到EntryList
  • Qmode == 4 && CXQ!=null将CXQ队列头插法插入到EntryList
  • 完成以上三步的操作之后(Qmode 默认值 0)
    • 如果CXQ==null,EntryList!=null,从EntryList队列中唤醒
    • 如果CXQ==null EntryList==null,continue执行

先将Monitor的owner设置为null,表示完全释放了锁对象(这时如果有其他线程进入同步块则能获得锁),相当于退出同步代码块。

  • 只有EntryList等于null的时候,而且Cxq不为空的时候才会执行下面的操作
    • Qmode == 1 将倒置CXQ队列,加入到EntryList中,然后从EntryList中唤醒线程
    • Qmode!=1 将CXQ队列,加入到EntryList中,然后从EntryList中唤醒线程
  • 其他的没说倒置,都是按照cxq的头尾顺序,也就是和cxq的顺序一致

唤醒过程总结为:

1、 退出同步代码块时 会让_recursions减1,当_recursions的值减为0时,说明(线程完全退出了同步代码块中)线程释放了锁;

2、 (释放完锁之后需要唤醒线程)根据不同的策略(策略不同唤醒不同的线程)(由QMode指定),从_cxq或_EntryList中获取头节点,通过ObjectMonitor::ExitEpilog方法唤醒该节点封装的线程,唤醒操作最终由unpack完成(将之前park的线程进行唤醒),实现如下

-----------------------------------------------------------------
# 截取部分代码
void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * wakee){
  assert( _owner == Self, "invariant");

  _succ = Knob_SuccEnabled ? wakee->_thread : NULL;
  ParkEvent * Trigger = wakee->_event;

  wakee = NULL;

  // Drop the lock
  OrderAccess::release_store_ptr(&_owner, NULL);
  OrderAccess::fence();           // ST _owner vs LD in unpark()

  if(SafepointSynchronize::do_call_back()){
    TEVENT(unpack before SAFEPOINT);
  }

  DTRACE_MONITOR_PROBE(contended__exit, this, object(), Self);
  Trigger->unpark();  // 唤醒之前被park()挂起的线程

  // Maintain stats and report events to JVMTI
  if (ObjectMonitor::_synch_Parks != NULL){
    ObjectMonitor::_sync_Parks->inc();
  }
}

notify唤醒

notify 或者 notifyAll 方法可以唤醒同一个锁监视器下调用 wait 挂起的线程,具体实现如下:

void ObjectMonitor::notify(TRAPS) {
    CHECK_OWNER();
    if (_WaitSet == NULL) {
        TEVENT (Empty - Notify);
        return;
    }
    DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);

    int Policy = Knob_MoveNotifyee;

    Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
    ObjectWaiter *iterator = DequeueWaiter();
    if (iterator != NULL) {
        // 省略一些代码

         // 头插 EntryList
        if (Policy == 0) {
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL;
                _EntryList = iterator;
            } else {
                List->_prev = iterator;
                iterator->_next = List;
                iterator->_prev = NULL;
                _EntryList = iterator;
            }
        } else if (Policy == 1) {      // 尾插 EntryList
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL;
                _EntryList = iterator;
            } else {
                ObjectWaiter *Tail;
                for (Tail = List; Tail->_next != NULL; Tail = Tail->_next);
                assert (Tail != NULL && Tail->_next == NULL, "invariant");
                Tail->_next = iterator;
                iterator->_prev = Tail;
                iterator->_next = NULL;
            }
        } else if (Policy == 2) {      // 头插 cxq
            // prepend to cxq
            if (List == NULL) {
                iterator->_next = iterator->_prev = NULL;
                _EntryList = iterator;
            } else {
                iterator->TState = ObjectWaiter::TS_CXQ;
                for (;;) {
                    ObjectWaiter *Front = _cxq;
                    iterator->_next = Front;
                    if (Atomic::cmpxchg_ptr(iterator, &_cxq, Front) == Front) {
                        break;
                    }
                }
            }
        } else if (Policy == 3) {      // 尾插 cxq
            iterator->TState = ObjectWaiter::TS_CXQ;
            for (;;) {
                ObjectWaiter *Tail;
                Tail = _cxq;
                if (Tail == NULL) {
                    iterator->_next = NULL;
                    if (Atomic::cmpxchg_ptr(iterator, &_cxq, NULL) == NULL) {
                        break;
                    }
                } else {
                    while (Tail->_next != NULL) Tail = Tail->_next;
                    Tail->_next = iterator;
                    iterator->_prev = Tail;
                    iterator->_next = NULL;
                    break;
                }
            }
        } else {
            ParkEvent *ev = iterator->_event;
            iterator->TState = ObjectWaiter::TS_RUN;
            OrderAccess::fence();
            ev->unpark();
        }

        if (Policy < 4) {
            iterator->wait_reenter_begin(this);
        }
    }
    // 自旋释放
    Thread::SpinRelease(&_WaitSetLock);

    if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
        ObjectMonitor::_sync_Notifications->inc();
    }
}

notify的策略

如果调用了Object中的wait方法,那么节点会转移到waitSet队列中,只有再次调用notify方法 才能将waitSet队列中的头节点放入entryList或者cxq,notifyAll 全部节点加入到cxq中(EntryList头结点线程叫做 OnDeck Thread,notifyAll不管cxq中是否为空,都会将待唤醒的线程节点都放到cxq中),然后在由线程释放锁之后进行唤醒On Deck线程。至于是否能够抢到锁,需要看CAS是否成功,因为可能刚好有新建的线程也通过CAS抢占Owner

  • Policy=0:将ObjectWaiter放入到enteylist队列的排头位置
  • Policy=1:放入到entrylist队列末尾位置
  • Policy=2(默认):判断entrylist是否为空,为空就放入到entrylist中,否则放入到cxq队列的排头位置
  • Policy=3:判断cxq是否为空,如果为空,直接放入头部,否则放入cxq队列末尾位置

注意: notify方法并不一定能够立刻唤醒调用Wait的线程。

总体逻辑示意图:

从上述默认的策略搭配来看:synchronized-monitor实现的互斥锁是后来居上的策略,也就是最新进入争用队列cxq的线程,优先被唤醒阻塞,而被notify的线程,则一般是进入EntryList成为OnDeck线程,优先级更高。

当我们修改openJdk的源码再编译后,可以将QMode强制设置为1,policy设置为1或者保持默认的2,可以得到近似的公平锁机制,只是被notify的线程仍然具有更高唤醒优先级。

所以在可以修改策略情况下,synchronized-monitor实现的互斥锁也可以做成公平锁机制。

AQS中的公平锁:

static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    final void lock() {
        acquire(1);
    }

    // AQS 继承过来的方法, 方便阅读, 放在此处
    public final void acquire(int arg) {
        if (
                !tryAcquire(arg) &&
                        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
        ) {
            selfInterrupt();
        }
    }
    // 与非公平锁主要区别在于 tryAcquire 方法的实现
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            // 先检查 AQS 队列中是否有前驱节点, 没有才去竞争
            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;
    }

    // ㈠ AQS 继承过来的方法, 方便阅读, 放在此处
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        // h != t 时表示队列中有 Node
        return h != t &&
                (
                        // (s = h.next) == null 表示队列中还有没有老二
                        (s = h.next) == null || // 或者队列中老二线程不是此线程
                                s.thread != Thread.currentThread()
                );
    }
}

其实就是新建的线程,如果有线程阻塞在争用队列CLH中(有前驱节点),就没有CAS的机会了,但是并不是先启动的线程就可以先入队,CAS和CPU调度的原因,不是先尝试入队,就一定先入队的。也不是绝对的公平的机制。

更多资源分享,请关注我的公众号:搜索或扫码 砥砺code

  • 55
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值