打通JAVA与内核!一个ReentrantLock锁的实现原理

跨域思考:构建程序员的通用建模心智模型 在分布式系统与多技术栈日益普及的今天,开发者面临的核心挑战已从‘掌握工具’转向‘理解范式’。数据库、编程语言、中间件等技术表象之下,共存着状态管理、并发控制、数据一致性、查询优化和资源调度五大底层计算范式。真正制约成长的并非知识广度,而是能否将MySQL的事务、Rust的所有权、Redis的原子操作等具体实现,抽象为对‘状态变更可逆性’‘共享资源协调’‘异常下数据确定性’等本质问题的统一认知。这种跨语言、跨存储的通用建模能力,支撑高可用架构设计、异构系统集成与性能瓶颈归因,是资深工程师突破职业平台期、实现技 阅读详情

写JAVA都知道,JAVA里的同学锁有几个类代码,是同步锁,魔法是并发包里的锁(JUC锁)。其中同步锁是JAVA语言文字提供的能力,在这个不展开,本文主要讨论JUC里的ReentrantLock锁。

一 JDK 层

1 AbstractQueuedSynchronizer

ReentrantLock的lock(),unlock()等API实际上依赖于内部的Synchronizer(注意,不是synchronized)来实现。Synchronizer又分为FairSync和NonfairSync,顾名思义是指公平和非公平。

当调用ReentrantLock的lock方法时,实际上只是简单地转给Synchronizer的lock()方法:

代码节选自:java.util.concurrent.locks.ReentrantLock.java
 /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
    /**
     * Base of synchronization control for this lock. Subclassed
     * into fair and nonfair versions below. Uses AQS state to
     * represent the number of holds on the lock.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
......
}


    public void lock() {
        sync.lock();
    }

那么这个sync又是什么?我们看到Sync继承自AbstractQueueSynchronizer(AQS),AQS是并发的基石,AQS不实现任何同步接口(比如lock,unlock,countDown等),但它定义了一个并发接口资源控制逻辑的框架(一式了模板方法设计模式),它定义了获取和释放方法用于独占地(独占)获取和释放资源,以及获取共享和释放共享方法用于共享地获取和释放资源。 release用于实现ReentrantLock,而
acquireShared/releaseShared用于实现CountDownLacth,Semaphore。比如acquire的框架如下:

    /**

     * Acquires in exclusive mode, ignoring interrupts.  Implemented

     * by invoking at least once {@link #tryAcquire},

     * returning on success.  Otherwise the thread is queued, possibly

     * repeatedly blocking and unblocking, invoking {@link

     * #tryAcquire} until success.  This method can be used

     * to implement method {@link Lock#lock}.

     *

     * @param arg the acquire argument.  This value is conveyed to

     *        {@link #tryAcquire} but is otherwise uninterpreted and

     *        can represent anything you like.

     */

    public final void acquire(int arg) {

        if (!tryAcquire(arg) &&

            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

            selfInterrupt();

    }

整体逻辑是,先进行一次tryAcquire,成功者,继续啥事了,调用了自己的代码,如果失败,则执行addWaiter和acquireQueued。其中tryAcquire()需要子类根据自己的同步需求进行acquireQueued()和addWaiter()已经由AQS实现。addWai的作用是把当前加入到AQS内部实现的线程,而acquireQueued的作用是当tryAcquire()失败的时候激发线程。

addWaiter 的代码如下:

/**

     * Creates and enqueues node for current thread and given mode.

     *

     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared

     * @return the new node

     */

    private Node addWaiter(Node mode) {

        //创建节点,设置关联线程和模式(独占或共享)

        Node node = new Node(Thread.currentThread(), mode);

        // Try the fast path of enq; backup to full enq on failure

        Node pred = tail;

        // 如果尾节点不为空,说明同步队列已经初始化过

        if (pred != null) {

            //新节点的前驱节点设置为尾节点

            node.prev = pred;

            // 设置新节点为尾节点

            if (compareAndSetTail(pred, node)) {

                //老的尾节点的后继节点设置为新的尾节点。 所以同步队列是一个双向列表。

                pred.next = node;

                return node;

            }

        }

        //如果尾节点为空,说明队列还未初始化,需要初始化head节点并加入新节点

        enq(node);

        return node;

    }

enq(node)的代码如下:

/**

     * Inserts node into queue, initializing if necessary. See picture above.

     * @param node the node to insert

     * @return node's predecessor

     */

    private Node enq(final Node node) {

        for (;;) {

            Node t = tail;

            if (t == null) { // Must initialize

                // 如果tail为空,则新建一个head节点,并且tail和head都指向这个head节点

                //队列头节点称作“哨兵节点”或者“哑节点”,它不与任何线程关联

                if (compareAndSetHead(new Node()))

                    tail = head;

            } else {

                //第二次循环进入这个分支,

                node.prev = t;

                if (compareAndSetTail(t, node)) {

                    t.next = node;

                    return t;

                }

            }

        }

    }

addWaiter执行结束后,同步驱动的结构如下所示:

 

acquireQueued 的代码如下:

 /**

     * Acquires in exclusive uninterruptible mode for thread already in

     * queue. Used by condition wait methods as well as acquire.

     *

     * @param node the node

     * @param arg the acquire argument

     * @return {@code true} if interrupted while waiting

     */

    final boolean acquireQueued(final Node node, int arg) {

        boolean failed = true;

        try {

            boolean interrupted = false;

            for (;;) {

                //获取当前node的前驱node

                final Node p = node.predecessor();

                //如果前驱node是head node,说明自己是第一个排队的线程,则尝试获锁

                if (p == head && tryAcquire(arg)) {

                    //把获锁成功的当前节点变成head node(哑节点)。

                    setHead(node);

                    p.next = null; // help GC

                    failed = false;

                    return interrupted;

                }

                if (shouldParkAfterFailedAcquire(p, node) &&

                    parkAndCheckInterrupt())

                    interrupted = true;

            }

        } finally {

            if (failed)

                cancelAcquire(node);

        }

    }

acquireQueued 的逻辑是:

判断自己是不是同步起源中的第一个过程自己的节点,则试点进行锁定,如果成功则将其变成头节点,如下所示:

 

如果自己不是第一个事件的节点或者tryAcqui失败,则调用shouldParkAfterFailed,其主要逻辑是使用CAS将节点状态由INITIAL设置成SIGNAL,显示当前线程等待SIGNAL。如果设置失败,会中acquire的循环中继续重试,直到设置成功,然后调用parkAndCheckInterrupt方法。parkAndCheckInterrupt的作用是当前线程模拟挂起,等待当前把parkAndCheckInterrupt的需要借助层的能力,这是这一点的重点,在中实现死层判断。

2 重入锁

下面就让我们一起来看看 ReentrantLock 是如何基于AbstractQueueSynchronizer 实现其输入的。

ReentrantLock内部使用的FairSync和NonfairSync,它们都是AQS的子类,比如FairSync的主要代码如下:

/**

     * Sync object for fair locks

     */

    static final class FairSync extends Sync {

        private static final long serialVersionUID = -3000897897090466540L;




        final void lock() {

            acquire(1);

        }




        /**

         * Fair version of tryAcquire.  Don't grant access unless

         * recursive call or no waiters or is first.

         */

        protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            if (c == 0) {

                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中最重要的一个领域就是状态,锁和同步器的实现都围绕着这个领域的修改展开。根据自己需要可以对同步状态的意义有不同的定义,并重新对应的tryAcquire、tryRelease或tryAcquireshared、tryReleaseShared等方法来操作同步状态。

我们来看看ReentrantLock的FairSync的tryAcquire的逻辑:

  1. 如果此时状态(private volatile int state)是0,那么就表示这个时候没有人占有锁。但因为是非锁,所以还要判断自己是首节点,然后才把实验状态设置为1,成功成功那么,就成功占有了锁。compareAndSetState也是通过CAS来实现的。CAS是原子操作,而且state的类型是volatile,所以state的值是线程安全的。
  2. 如果此时不是状态,那么再判断当前线程是不是锁的主人,如果是的话,则状态会启动,当状态时,会抛错。
  3. 获得不满足,则返回假,获取锁失败。

至此,整个JAVA语言的实现基本说清楚了,小结一下,框架如下所示:

 

关于解锁的实现,只有篇幅,还有讨论了,重点分析锁中是如何把当前的场景演示挂起的,上上的unsafe.park()是如何实现的。

二、JVM层

Unsafe.park和Unsafe.unpark是sun.misc.Unsafe类的原生方法,

public native void unpark(Object var1);




public native void park(boolean var1, long var2);

这两种方法的实现是在JVM的
hotspot/src/share/vm/prims/unsafe.cpp文件中,

UNSAFE_ENTRY(void, Unsafe_Park(JNIEnv *env, jobject unsafe, jboolean isAbsolute, jlong time))

  UnsafeWrapper("Unsafe_Park");

  EventThreadPark event;

#ifndef USDT2

  HS_DTRACE_PROBE3(hotspot, thread__park__begin, thread->parker(), (int) isAbsolute, time);

#else /* USDT2 */

   HOTSPOT_THREAD_PARK_BEGIN(

                             (uintptr_t) thread->parker(), (int) isAbsolute, time);

#endif /* USDT2 */

  JavaThreadParkedState jtps(thread, time != 0);

  thread->parker()->park(isAbsolute != 0, time);

#ifndef USDT2

  HS_DTRACE_PROBE1(hotspot, thread__park__end, thread->parker());

#else /* USDT2 */

  HOTSPOT_THREAD_PARK_END(

                          (uintptr_t) thread->parker());

#endif /* USDT2 */

  if (event.should_commit()) {

    const oop obj = thread->current_park_blocker();

    if (time == 0) {

      post_thread_park_event(&event, obj, min_jlong, min_jlong);

    } else {

      if (isAbsolute != 0) {

        post_thread_park_event(&event, obj, min_jlong, time);

      } else {

        post_thread_park_event(&event, obj, time, min_jlong);

      }

    }

  }

UNSAFE_END

 

 核心是逻辑是thread->parker()->park(isAbsolute != 0, time); 就是获取java线程的parker对象,然后执行它的park方法。

class Parker : public os::PlatformParker {

private:

  volatile int _counter ;

  ...

public:

  void park(bool isAbsolute, jlong time);

  void unpark();

  ...

}

class PlatformParker : public CHeapObj<mtInternal> {

  protected:

    enum {

        REL_INDEX = 0,

        ABS_INDEX = 1

    };

    int _cur_index;  // which cond is in use: -1, 0, 1

    pthread_mutex_t _mutex [1] ;

    pthread_cond_t  _cond  [2] ; // one for relative times and one for abs.




  public:       // TODO-FIXME: make dtor private

    ~PlatformParker() { guarantee (0, "invariant") ; }




  public:

    PlatformParker() {

      int status;

      status = pthread_cond_init (&_cond[REL_INDEX], os::Linux::condAttr());

      assert_status(status == 0, status, "cond_init rel");

      status = pthread_cond_init (&_cond[ABS_INDEX], NULL);

      assert_status(status == 0, status, "cond_init abs");

      status = pthread_mutex_init (_mutex, NULL);

      assert_status(status == 0, status, "mutex_init");

      _cur_index = -1; // mark as unused

    }

};
公园方法:
void Parker::park(bool isAbsolute, jlong time) {

  // Return immediately if a permit is available.

  // We depend on Atomic::xchg() having full barrier semantics

  // since we are doing a lock-free update to _counter.

  if (Atomic::xchg(0, &_counter) > 0) return;




  Thread* thread = Thread::current();

  assert(thread->is_Java_thread(), "Must be JavaThread");

  JavaThread *jt = (JavaThread *)thread;




  if (Thread::is_interrupted(thread, false)) {

    return;

  }




  // Next, demultiplex/decode time arguments

  timespec absTime;

  if (time < 0 || (isAbsolute && time == 0) ) { // don't wait at all

    return;

  }

  if (time > 0) {

    unpackTime(&absTime, isAbsolute, time);

  }




  ////进入safepoint region,更改线程为阻塞状态

  ThreadBlockInVM tbivm(jt);

  // Don't wait if cannot get lock since interference arises from

  // unblocking.  Also. check interrupt before trying wait

  if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) != 0) {

  //如果线程被中断,或者尝试给互斥变量加锁时失败,比如被其它线程锁住了,直接返回

    return;

  }

  //到这里,意味着pthread_mutex_trylock(_mutex)成功

  int status ;

  if (_counter > 0)  { // no wait needed

    _counter = 0;

    status = pthread_mutex_unlock(_mutex);

    assert (status == 0, "invariant") ;

    OrderAccess::fence();

    return;

  }




#ifdef ASSERT

  // Don't catch signals while blocked; let the running threads have the signals.

  // (This allows a debugger to break into the running thread.)

  sigset_t oldsigs;

  sigset_t* allowdebug_blocked = os::Linux::allowdebug_blocked_signals();

  pthread_sigmask(SIG_BLOCK, allowdebug_blocked, &oldsigs);

#endif

  OSThreadWaitState osts(thread->osthread(), false /* not Object.wait() */);

  jt->set_suspend_equivalent();

  // cleared by handle_special_suspend_equivalent_condition() or java_suspend_self()




  assert(_cur_index == -1, "invariant");

  if (time == 0) {

    _cur_index = REL_INDEX; // arbitrary choice when not timed

    status = pthread_cond_wait (&_cond[_cur_index], _mutex) ;

  } else {

    _cur_index = isAbsolute ? ABS_INDEX : REL_INDEX;

    status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime) ;

    if (status != 0 && WorkAroundNPTLTimedWaitHang) {

      pthread_cond_destroy (&_cond[_cur_index]) ;

      pthread_cond_init    (&_cond[_cur_index], isAbsolute ? NULL : os::Linux::condAttr());

    }

  }

  _cur_index = -1;

  assert_status(status == 0 || status == EINTR ||

                status == ETIME || status == ETIMEDOUT,

                status, "cond_timedwait");




#ifdef ASSERT

  pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);

#endif




  _counter = 0 ;

  status = pthread_mutex_unlock(_mutex) ;

  assert_status(status == 0, status, "invariant") ;

  // Paranoia to ensure our locked and lock-free paths interact

  // correctly with each other and Java-level accesses.

  OrderAccess::fence();




  // If externally suspended while waiting, re-suspend

  if (jt->handle_special_suspend_equivalent_condition()) {

    jt->java_suspend_self();

  }

}

park的思路:parker内部有关键字段_计数器,一个计数器记录的“许可”,当_计数器大于0时,有许可,然后就可以把_计数器设置为0,是获得了许可,可以继续运行过去的代码。如果此时_计数器不大于0,则等待这个条件满足。

下面我具体来看看公园的具体实现:

  1. 当调用park,先尝试可能成功实现“许可”,即,如果,则把_counter设置为0,并返回。
  2. 如果不成功,则将线程的设置成_thread_in_vm并且_thread_blocked。_thread_in_vm表示线程当前在JVM中执行,_thread_blocked表示线程状态当前模式了。
  3. mutex之后,再次检查_counter是不是>0,如果是,则把_counter设置为0,解锁mutex并返回
  4. 如果_counter还是不大于0,则判断等待的时间等于0,然后调用相应的pthread_cond_wait系列函数进行等待,如果等待返回(即有人进行unpark,则pthread_cond_signal来通知),则把_counter设置为0,解锁互斥并返回。

所以上本质原因,LockSupport.park是通过pthread库的条件pthread_cond_t来实现的。下面我们就来看看pthread_cond_t是怎么实现的。

三 GLIBC 层

pthread_cond_t 典型的用法如下:

#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>




pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/

pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  //初始化条件变量 




void *thread1(void *);

void *thread2(void *);




int i=1;

int main(void)

{

        pthread_t t_a;

        pthread_t t_b;

        pthread_create(&t_a,NULL,thread1,(void *)NULL);/*创建进程t_a*/

        pthread_create(&t_b,NULL,thread2,(void *)NULL); /*创建进程t_b*/

        pthread_join(t_b, NULL);/*等待进程t_b结束*/

        pthread_mutex_destroy(&mutex);

        pthread_cond_destroy(&cond);

        exit(0);

}

void *thread1(void *junk)

{

        for(i=1;i<=9;i++)

        {

             pthread_mutex_lock(&mutex);//

             if(i%3==0)

                 pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b进程*/

              else       

                  printf("thead1:%d/n",i);

              pthread_mutex_unlock(&mutex);//*解锁互斥量*/

              printf("Up Unlock Mutex/n");      

              sleep(1);

        }

}

void *thread2(void *junk)

{

        while(i<9)

        {

            pthread_mutex_lock(&mutex);

            if(i%3!=0)

                   pthread_cond_wait(&cond,&mutex);/*等待*/

            printf("thread2:%d/n",i);

            pthread_mutex_unlock(&mutex);

            printf("Down Ulock Mutex/n");

            sleep(1);

         }




}

重点就是:无论是
pthread_cond_waitpthread_cond_signal都必须得先pthread_mutex_lock。如果没有这个保护,可能会产生竞态条件,漏掉信号。pthread_cond_wait()函数一进入wait状态会自动释放互斥锁。当其他线程通过pthread_cond_signal或pthread_cond_broadcast 使该线程启动,使 pthread_cond_wait()返回时,该线程又自动获得该互斥锁。

整个过程如下图所示:

 

1 pthread_mutex_lock

例如,在Linux中,使用了一个Futex(快速用户空间互斥锁的部分)的。

在此系统中,对用户空间中的互斥启动原子和测试操作。

如果操作结果证明锁上没有争用,则对pthread_mutex_lock的调用将返回,而无需可以通信到内核中,因此获得互斥量的操作非常快。

仅当检测到争用时,系统调用(以后futex)才会发生,并且上下文切换到内核中,使这调用进程进入睡眠状态,直到解除互锁直到。

还有很多更多的细节,尤其是可以相信和/或优先级继承反对,但这只是它的本质。

nptl/pthread_mutex_lock.c

int

PTHREAD_MUTEX_LOCK (pthread_mutex_t *mutex)

{

  /* See concurrency notes regarding mutex type which is loaded from __kind

     in struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h.  */

  unsigned int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);




  LIBC_PROBE (mutex_entry, 1, mutex);




  if (__builtin_expect (type & ~(PTHREAD_MUTEX_KIND_MASK_NP

                                 | PTHREAD_MUTEX_ELISION_FLAGS_NP), 0))

    return __pthread_mutex_lock_full (mutex);




  if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_NP))

    {

      FORCE_ELISION (mutex, goto elision);

    simple:

      /* Normal mutex.  */

      LLL_MUTEX_LOCK_OPTIMIZED (mutex);

      assert (mutex->__data.__owner == 0);

    }

#if ENABLE_ELISION_SUPPORT

  else if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_ELISION_NP))

    {

  elision: __attribute__((unused))

      /* This case can never happen on a system without elision,

         as the mutex type initialization functions will not

         allow to set the elision flags.  */

      /* Don't record owner or users for elision case.  This is a

         tail call.  */

      return LLL_MUTEX_LOCK_ELISION (mutex);

    }

#endif

  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)

                             == PTHREAD_MUTEX_RECURSIVE_NP, 1))

    {

      /* Recursive mutex.  */

      pid_t id = THREAD_GETMEM (THREAD_SELF, tid);




      /* Check whether we already hold the mutex.  */

      if (mutex->__data.__owner == id)

        {

          /* Just bump the counter.  */

          if (__glibc_unlikely (mutex->__data.__count + 1 == 0))

            /* Overflow of the counter.  */

            return EAGAIN;




          ++mutex->__data.__count;




          return 0;

        }




      /* We have to get the mutex.  */

      LLL_MUTEX_LOCK_OPTIMIZED (mutex);




      assert (mutex->__data.__owner == 0);

      mutex->__data.__count = 1;

    }

  else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)

                          == PTHREAD_MUTEX_ADAPTIVE_NP, 1))

    {

      if (LLL_MUTEX_TRYLOCK (mutex) != 0)

        {

          int cnt = 0;

          int max_cnt = MIN (max_adaptive_count (),

                             mutex->__data.__spins * 2 + 10);

          do

            {

              if (cnt++ >= max_cnt)

                {

                  LLL_MUTEX_LOCK (mutex);

                  break;

                }

              atomic_spin_nop ();

            }

          while (LLL_MUTEX_TRYLOCK (mutex) != 0);




          mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;

        }

      assert (mutex->__data.__owner == 0);

    }

  else

    {

      pid_t id = THREAD_GETMEM (THREAD_SELF, tid);

      assert (PTHREAD_MUTEX_TYPE (mutex) == PTHREAD_MUTEX_ERRORCHECK_NP);

      /* Check whether we already hold the mutex.  */

      if (__glibc_unlikely (mutex->__data.__owner == id))

        return EDEADLK;

      goto simple;

    }




  pid_t id = THREAD_GETMEM (THREAD_SELF, tid);




  /* Record the ownership.  */

  mutex->__data.__owner = id;

#ifndef NO_INCR

  ++mutex->__data.__nusers;

#endif




  LIBC_PROBE (mutex_acquired, 1, mutex);




  return 0;

}

pthread_mutex_t 的定义如下:

typedef union

{

  struct __pthread_mutex_s

  {

    int __lock;

    unsigned int __count;

    int __owner;

    unsigned int __nusers;

    int __kind;

    int __spins;

    __pthread_list_t __list;

  } __data;

  ......

} pthread_mutex_t;

__一种类型是指锁的类型,取值如下:

/* Mutex types.  */

enum

{ 

  PTHREAD_MUTEX_TIMED_NP,

  PTHREAD_MUTEX_RECURSIVE_NP,

  PTHREAD_MUTEX_ERRORCHECK_NP,

  PTHREAD_MUTEX_ADAPTIVE_NP

#if defined __USE_UNIX98 || defined __USE_XOPEN2K8

  ,

  PTHREAD_MUTEX_NORMAL = PTHREAD_MUTEX_TIMED_NP,

  PTHREAD_MUTEX_RECURSIVE = PTHREAD_MUTEX_RECURSIVE_NP,

  PTHREAD_MUTEX_ERRORCHECK = PTHREAD_MUTEX_ERRORCHECK_NP,

  PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL

#endif

#ifdef __USE_GNU

  /* For compatibility.  */

  , PTHREAD_MUTEX_FAST_NP = PTHREAD_MUTEX_TIMED_NP

#endif

};

其中: 

  • PTHREAD_MU_TIMED_NP,这是一种值,也就是普通锁。 

  • PTHREAD_MUTEX_RECURSIVE_NP允许可重入锁,一个线程对同一个锁成功获得多次,并通过多次解锁重启。

  • PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程重复请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型相同。

  • PTHREAD_MUTEX_ADAPTIVE_NP,惯锁,自旋锁与普通锁的混合。 

互斥体默认使用的是 PTHREAD_MUTEX_TIMED_NP,所以会走 LLL_MUTEX_LOCK_OPTIMIZED,这是个宏:

# define LLL_MUTEX_LOCK_OPTIMIZED(mutex) lll_mutex_lock_optimized (mutex)
lll_mutex_lock_optimized (pthread_mutex_t *mutex)

{

  /* The single-threaded optimization is only valid for private

     mutexes.  For process-shared mutexes, the mutex could be in a

     shared mapping, so synchronization with another process is needed

     even without any threads.  If the lock is already marked as

     acquired, POSIX requires that pthread_mutex_lock deadlocks for

     normal mutexes, so skip the optimization in that case as

     well.  */

  int private = PTHREAD_MUTEX_PSHARED (mutex);

  if (private == LLL_PRIVATE && SINGLE_THREAD_P && mutex->__data.__lock == 0)

    mutex->__data.__lock = 1;

  else

    lll_lock (mutex->__data.__lock, private);

}

因为不是LLL_PRIVATE,所以走lll_lock,lll_lock也是个宏:

#define lll_lock(futex, private)        \

  __lll_lock (&(futex), private)

注意这里出现了文字,本文的重点主要是围绕它展开的。

#define __lll_lock(futex, private)                                      \

  ((void)                                                               \

   ({                                                                   \

     int *__futex = (futex);                                            \

     if (__glibc_unlikely                                               \

         (atomic_compare_and_exchange_bool_acq (__futex, 1, 0)))        \

       {                                                                \

         if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \

           __lll_lock_wait_private (__futex);                           \

         else                                                           \

           __lll_lock_wait (__futex, private);                          \

       }                                                                \

   }))

  其中,
atomic_compare_and_exchange_bool_acq是实验如下通过原子操作实验将__futex(mutex->__data.__lock)从0变为1,如果成功就直接返回了,如果失败,则调用__lll_lock_wait,代码:

void

__lll_lock_wait (int *futex, int private)

{

  if (atomic_load_relaxed (futex) == 2)

    goto futex;




  while (atomic_exchange_acquire (futex, 2) != 0)

    {

    futex:

      LIBC_PROBE (lll_lock_wait, 1, futex);

      futex_wait ((unsigned int *) futex, 2, private); /* Wait if *futex == 2.  */

    }

}

在这里先要说明一下,pthread将futex的锁状态定义为3种:

  • 0,代表当前锁开启无锁,可以进行快速上锁,无需进门。

  • 1,代表有线程抓住当前锁,如果有线程线程需要上锁,就必须标记futex为“锁竞争”,然后通过futex系统调用进内核把当前线程挂起。

  • 2,更换锁竞争,有其他线程将要或正在内核的futex系统中等待锁定。

上锁失败进入到__lll_lock_wait后,先判断futex是不是等于2,那么说明大家都在这里所以如果你也排着吧(直接跳转到futex_wait)。如果不等于2,那说明你是第一个来设置自己的人,把futex 2,告诉对方来的人要结婚,然后以身作则先结婚。

futex_wait 就是调用futex系统调用在第四节,我们就来仔细分析这个系统。

2 pthread_cond_wait

本质也是需要futex系统调用,只有篇幅不展开了。

四面层

什么时候加入内核的?

简单简单,futex 的解决思路是:在无战斗的情况下操作完全在用户空间进行,无需系统调用,仅在发生或者发生的时候就进入昏迷去完成相应的处理(等待醒来)。所以说,futex是一种用户模式和内核模式混合的同步机制,需要两种模式合作才能完成,futex创建位于用户空间,而不是代码内核对象,futex的也分为用户模式和内核模式两部分,无竞争的情况下在用户模式,发生竞争时则通过sys_futex系统调用进入内核模式进行处理。

已经在前面演示用户了,本节重点讲解futex在内核部分的实现。

futex 设计了三个基本数据结构:futex_hash_bucket,futex_key,futex_q。

struct futex_hash_bucket {

        atomic_t waiters;

        spinlock_t lock;

        struct plist_head chain;

} ____cacheline_aligned_in_smp;
struct futex_q {

        struct plist_node list;

        struct task_struct *task;

        spinlock_t *lock_ptr;

        union futex_key key;   //唯一标识uaddr的key值

        struct futex_pi_state *pi_state;

        struct rt_mutex_waiter *rt_waiter;

        union futex_key *requeue_pi_key;

        u32 bitset;

};

 

 

union futex_key { 

        struct {

                unsigned long pgoff;

                struct inode *inode;

                int offset;

        } shared;

        struct {

                unsigned long address;

                struct mm_struct *mm;

                int offset;

        } private; 

        struct {

                unsigned long word;

                void *ptr;

                int offset;

        } both;

};

其实还有个struct __futex_data,如下所示,这个

static struct {

        struct futex_hash_bucket *queues;

        unsigned long            hashsize;

} __futex_data __read_mostly __aligned(2*sizeof(long));




#define futex_queues   (__futex_data.queues)

#define futex_hashsize (__futex_data.hashsize)

在futex初始化的时候(futex_init),会确定hashsize,比如24核cpu时,hashsize = 8192。然后根据这个hashsize调用alloc_large_system_hash分配数组空间,并初始化数组元素里的相关属性,比如plist_head,lock。

static int __init futex_init(void)

{

        unsigned int futex_shift;

        unsigned long i;




#if CONFIG_BASE_SMALL

        futex_hashsize = 16;

#else

        futex_hashsize = roundup_pow_of_two(256 * num_possible_cpus());

#endif




        futex_queues = alloc_large_system_hash("futex", sizeof(*futex_queues),

                                               futex_hashsize, 0,

                                               futex_hashsize < 256 ? HASH_SMALL : 0,

                                               &futex_shift, NULL,

                                               futex_hashsize, futex_hashsize);

        futex_hashsize = 1UL << futex_shift;




        futex_detect_cmpxchg();




        for (i = 0; i < futex_hashsize; i++) {

                atomic_set(&futex_queues[i].waiters, 0);

                plist_head_init(&futex_queues[i].chain);

                spin_lock_init(&futex_queues[i].lock);

        }




        return 0;

}

 

这些数据结构之间的关系如下所示:

 

脑子里有了数据结构,流程就容易理解了。futex_wait的总体流程如下:

 

 

static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,

                      ktime_t *abs_time, u32 bitset)

{

        struct hrtimer_sleeper timeout, *to = NULL;

        struct restart_block *restart;

        struct futex_hash_bucket *hb;

        struct futex_q q = futex_q_init;

        int ret;




        if (!bitset)

                return -EINVAL;

        q.bitset = bitset;




        if (abs_time) {

                to = &timeout;

                hrtimer_init_on_stack(&to->timer, (flags & FLAGS_CLOCKRT) ?

                                      CLOCK_REALTIME : CLOCK_MONOTONIC,

                                      HRTIMER_MODE_ABS);

                hrtimer_init_sleeper(to, current);

                hrtimer_set_expires_range_ns(&to->timer, *abs_time,

                                             current->timer_slack_ns);

        }




retry:

        /*

         * Prepare to wait on uaddr. On success, holds hb lock and increments

         * q.key refs.

         */

        ret = futex_wait_setup(uaddr, val, flags, &q, &hb);

        if (ret)

                goto out;




        /* queue_me and wait for wakeup, timeout, or a signal. */

        futex_wait_queue_me(hb, &q, to);




        /* If we were woken (and unqueued), we succeeded, whatever. */

        ret = 0;

        /* unqueue_me() drops q.key ref */

        if (!unqueue_me(&q))

                goto out;

        ret = -ETIMEDOUT;

        if (to && !to->task)

                goto out;




        /*

         * We expect signal_pending(current), but we might be the

         * victim of a spurious wakeup as well.

         */

        if (!signal_pending(current))

                goto retry;




        ret = -ERESTARTSYS;

        if (!abs_time)

                goto out;




        restart = ¤t->restart_block;

        restart->fn = futex_wait_restart;

        restart->futex.uaddr = uaddr;

        restart->futex.val = val;

        restart->futex.time = *abs_time;

        restart->futex.bitset = bitset;

        restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;




        ret = -ERESTART_RESTARTBLOCK;




out:

        if (to) {

                hrtimer_cancel(&to->timer);

                destroy_hrtimer_on_stack(&to->timer);

        }

        return ret;

}

 函数futex_wait_setup主要做两件事,一是对uaddr进行hash,找到futex_hash_bucket并获取它上面的自旋锁,二是判断*uaddr是否为预期值。如果没有被录取立即返回,由用户状态继续trylock。

*

 * futex_wait_setup() - Prepare to wait on a futex

 * @uaddr:      the futex userspace address

 * @val:        the expected value

 * @flags:      futex flags (FLAGS_SHARED, etc.)

 * @q:          the associated futex_q

 * @hb:         storage for hash_bucket pointer to be returned to caller

 *

 * Setup the futex_q and locate the hash_bucket.  Get the futex value and

 * compare it with the expected value.  Handle atomic faults internally.

 * Return with the hb lock held and a q.key reference on success, and unlocked

 * with no q.key reference on failure.

 *

 * Return:

 *  -  0 - uaddr contains val and hb has been locked;

 *  - <1 - -EFAULT or -EWOULDBLOCK (uaddr does not contain val) and hb is unlocked

 */

static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,

                           struct futex_q *q, struct futex_hash_bucket **hb)

{

        u32 uval;

        int ret;        

retry:

        //初始化futex_q, 把uaddr设置到futex_key的字段中,将来futex_wake时也是通过这个key来查找futex。

        ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &q->key, VERIFY_READ);

        if (unlikely(ret != 0))

                return ret;




retry_private:

        //根据key计算hash,然后在数组里找到对应的futex_hash_bucket

        *hb = queue_lock(q);

        //原子地将uaddr的值读到uval中

        ret = get_futex_value_locked(&uval, uaddr);




        if (ret) {

                queue_unlock(*hb);




                ret = get_user(uval, uaddr);

                if (ret)

                        goto out;




                if (!(flags & FLAGS_SHARED))

                        goto retry_private;




                put_futex_key(&q->key);

                goto retry;

        }

        //如果当前uaddr指向的值不等于val,即说明其他进程修改了

  //uaddr指向的值,等待条件不再成立,不用阻塞直接返回。

       if (uval != val) {

                queue_unlock(*hb);

                ret = -EWOULDBLOCK;

        }




out:

        if (ret)

                put_futex_key(&q->key);

        return ret;

}

然后调用futex_wait_queue_me 把当前进程挂起:

/**

 * futex_wait_queue_me() - queue_me() and wait for wakeup, timeout, or signal

 * @hb:         the futex hash bucket, must be locked by the caller

 * @q:          the futex_q to queue up on

 * @timeout:    the prepared hrtimer_sleeper, or null for no timeout

 */

static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,

                                struct hrtimer_sleeper *timeout)

{

        /*

         * The task state is guaranteed to be set before another task can

         * wake it. set_current_state() is implemented using smp_store_mb() and

         * queue_me() calls spin_unlock() upon completion, both serializing

         * access to the hash list and forcing another memory barrier.

         */

        set_current_state(TASK_INTERRUPTIBLE);

        queue_me(q, hb);




        /* Arm the timer */

        if (timeout)

                hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);




        /*

         * If we have been removed from the hash list, then another task

         * has tried to wake us, and we can skip the call to schedule().

         */

        if (likely(!plist_node_empty(&q->list))) {

                /*

                 * If the timer has already expired, current will already be

                 * flagged for rescheduling. Only call schedule if there

                 * is no timeout, or if it has yet to expire.

                 */

                if (!timeout || timeout->task)

                        freezable_schedule();

        }

        __set_current_state(TASK_RUNNING);

}

futex_wait_queue_me我主要做几件事:

  1. 将当前线程插入到延迟,就是挂到futex_hash_bucket上
  2. 启动定时任务
  3. 主动触发进程调度

五总结

本文主要是对JAVA中的ReentrantLock.lock流程进行了自上而下的极限。

钉钉Java面经及参考答案 TCP 三次握手建立连接的核心目标是双向确认双方发送、接收能力,同步通信序列号,协商初始窗口大小,防止历史过期连接报文干扰新连接。第一次握手,客户端主动向服务端发送 SYN 报文,携带客户端随机生成的初始序列号 ISN,客户端状态变更为 SYN_SENT,目的是告知服务端客户端具备发送数据的能力。第二次握手,服务端收到 SYN 报文后,回复 SYN+ACK 报文,其中 ACK 确认号为客户端 ISN+1,同时携带服务端自身随机初始序列号,服务端进入 SYN_RCVD 状态,代表服务端确认收到客户端报文,且自 阅读详情

相关推荐

简化神经元模型6 -- Hindmarsh-Rose Model

简化神经元模型 6 Hindmarsh-Rose Model

weixin_73557167的博客 1427

一文彻底理解ReentrantLock可重入锁的使用

https://blog.youkuaiyun.com/zhizhengguan/article/details/107321574

文盲青年的博客 552

ADAS系统架构与核心功能解析:从感知到执行的智能驾驶方案

高级驾驶员辅助系统(ADAS)是现代汽车智能化的核心技术,其本质是通过传感器融合、算法决策与车辆控制,构建一个“感知-决策-执行”的闭环系统。在技术原理上,ADAS依赖摄像头、毫米波雷达、激光雷达等多源传感器进行环境感知,并通过深度学习模型与融合算法(如卡尔曼滤波)构建精准的环境模型。其技术价值在于提供主动安全冗余与驾驶舒适性,核心应用场景包括自动紧急制动(AEB)、自适应巡航(ACC)及车道保持辅助(LKA)等。随着传感器成本下降与域控制器算力提升,ADAS正从高端配置向主流车型快速普及,并逐步向高阶导航

weixin_31062533的博客 418

打通JAVA与内核,一个ReentrantLock锁的实现原理。

写JAVA都知道,JAVA里的同学锁有几个类代码,是同步锁,魔法是并发包里的锁(JUC锁)。其中同步锁是JAVA语言文字提供的能力,在这个不展开,本文主要讨论JUC里的ReentrantLock锁。 一 JDK 层 1 AbstractQueuedSynchronizer ReentrantLock的lock(),unlock()等API实际上依赖于内部的Synchronizer(注意,不是synchronized)来实现。Synchronizer又分为FairSync和NonfairSy...

Javaesandyou的博客 484

显著性算法 + 伪彩变换

利用opencv得到显著图和伪彩图

正在挣扎中的人 2298

win 11bitlocker恢复密匙一般情况的解决方式(这里指的是你现在使用的微软账户一直没有变更过)

本来没有打算写这一篇解决帖子的,但是最近好多微博的友友都在私我询问解决方法,孩子虽然很热心,但是真的回复不过来了,打字太累了,这里给大家简单指个路,希望能够对大家有所帮助。 当时出现这个问题的时候,一搜网上的解决方法都是重装系统什么的,真是吓死孩子啦。后来去微软的官网倒腾了半天解决了问题。其实只要你没有改过微软账户,你可以直接试试我这个方法,大概三到五分钟解决问题吧。 和我情况不一样的也可以参考一下贴吧里一些大佬的分享,或者实在着急我感觉去淘宝上找个解决也不是未尝不可。 出现问题: 解决方案: 直接去微软

zhiqingxue的博客 2万+

ReentrantLock 基本原理

ReentrantLock底层基于AQS,其构造方法返回的就是NonfaireSync和faireSync;两种同步器都继承自Sync,Sync又继承自AQS!newReentrantLock时,返回的就是同步器,默认是非公平的;

机械工程跑路哥 3616

嵌入式存储入门:Emmc控制器与设备工作原理全解析(附Synopsys/Arasan架构图)

本文深入解析了嵌入式系统中Emmc控制器与设备的工作原理,重点剖析了其作为CPU与存储设备桥梁的核心架构。通过对比Synopsys和Arasan两家主流IP厂商的控制器设计,详细阐述了DMA引擎与FIFO缓冲模块如何协同工作以提升数据传输效率,并提供了从硬件PCB布局到驱动调试的实战指南,帮助硬件工程师与开发者优化系统性能。

weixin_29248653的博客 146

ReentrantLock可重入锁实现指定粒度的锁控制

单体服务,该机制建立在单体服务的基础上实现的,如果是分布式服务,建议使用分布式锁。项目问题如下:积分兑换奖品,奖品有库存和兑换限制,每日奖品限量,且每家庭每个奖品只能兑换一次。如果在方法使用synchronized关键字,则所有奖品兑换都会变成串行,导致兑换逻辑排队,在并发兑换时严重影响用户体验。解决思路:使用ReentrantLock可重入锁来更加灵活的控制加锁粒度。新的问题:如果直接在逻辑中创建一个新的ReentrantLock并且尝试获取锁,无法指定锁对应的奖品类目,进而无法控制库存。

darkendon的博客 484

Claude Code 错误解决方案 使用策略拒绝 Usage Policy refusal

摘要:本文分析了Claude Code API请求被拒绝的问题,当违反Usage Policy时会显示错误提示。根因包括请求内容违规、模型敏感度差异或历史消息影响。解决方案包括修改请求措辞、切换至Sonnet 4.5模型或开启新会话。文章详细追踪了错误链路,提供了核心代码分析,并给出验证方法和最佳实践建议,帮助用户避免触发API拒绝机制。

多媒体编程、网络编程、系统编程、网络安全编程、驱动编程 980

161、车载环视系统的ISP多路同步架构——海思Hi3516DV300的4路鱼眼校正与拼接的帧同步误差控制

文章摘要: 针对海思Hi3516DV300车载环视系统四路鱼眼摄像头拼接画面动态撕裂问题,分析发现根本原因为帧同步误差(部分通道帧起始时间差1.2ms)。关键解决措施包括:1)通过VI通道主从同步模式(MASTER/SLAVE)结合软件补偿控制帧延迟;2)优化VPSS通道帧率锁定与丢帧策略确保拼接时序一致;3)严格校准鱼眼校正LUT参数与产线标定数据匹配;4)建议采用硬件同步信号(如sensor的FSIN引脚)降低软件同步压力。调试阶段需通过示波器直接测量sensor VSYNC信号,量产时需注意动态切换分

zzfmlxs的博客 136

锁-ReentrantLock

ReentrantLock

weixin_41998947的博客 835

YOLO26涨点改进 | 全网独家创新!注意力改进篇 | CVPR 2025 | 引入RALA秩增强线性注意力模块,含多种创新改进,助力YOLO26适合目标检测、图像分割、图像分类有效涨点

🔥本文给大家介绍使用RALA模块改进YOLO26可以显著提升其性能,尤其在复杂场景和小物体检测中。通过增强线性注意力机制的矩阵秩,RALA改善了模型对空间信息的建模能力,提升了特征的多样性和全局信息的融合,进而提高了检测精度。同时,RALA保持了线性复杂度,避免了Softmax注意力的高计算成本,使YOLO26在实时检测任务中既能保持高效计算,又能提高检测精度,尤其是在小物体和遮挡物体的检测中表现突出。🔥欢迎订阅我的专栏、带你学习使用最新-最前沿-独家YOLO26创新改进!

Ai缝合怪、助力小伙伴高效发论文 143

打通JAVA与内核系列之一ReentrantLock锁的实现原理

简介:写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁)。其中synchronized锁是JAVA语言层面提供的能力,在此不展开,本文主要讨论JUC里的ReentrantLock锁。 作者 | 蒋冲 来源 | 阿里技术公众号 写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁)。其中synchronized锁是JAVA语言层面提供的能力,在

阿里云云栖号 1129

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

打通JAVA与内核。一个ReentrantLock锁的实现原理

写JAVA都知道,JAVA里的同学锁有几个类代码,是同步锁,魔法是并发包里的锁(JUC锁)。其中同步锁是JAVA语言文字提供的能力,在这个不展开,本文主要讨论JUC里的ReentrantLock锁。 一 JDK 层 1 AbstractQueuedSynchronizer ReentrantLock的lock(),unlock()等API实际上依赖于内部的Synchronizer(注意,不是synchronized)来实现。Synchronizer又分为FairSync和NonfairSync..

ma_xiao_qi的博客 238

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

打通JAVA与内核,一个ReentrantLock锁的实现原理。,java面试算法题

小编在这里分享些我自己平时的学习资料,由于篇幅限制,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!程序员代码面试指南 IT名企算法与数据结构题目最优解这是” 本程序员面试宝典!书中对IT名企代码面试各类题目的最优解进行了总结,并提供了相关代码实现。针对当前程序员面试缺乏权威题目汇总这一-痛点, 本书选取将近200道真实出现过的经典代码面试题,帮助广“大程序员的面试准备做到万无一失。“刷”完本书后,你就是“题王”!

2401_84411849的博客 836

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

打通 JAVA 与内核系列之 一 ReentrantLock 锁的实现原理

写JAVA代码的同学都知道,JAVA里的锁有两大类,一类是synchronized锁,一类是concurrent包里的锁(JUC锁)。其中synchronized锁是JAVA语言层面提供的能力,在此不展开,本文主要讨论JUC里的ReentrantLock锁。

数据库技术 193

Java校招突击指南:高频考点串联与场景题破题策略

在计算机科学和软件工程领域,数据结构与算法、并发编程、数据库原理及框架设计是构建高性能、高可用系统的核心基础。理解这些基础概念及其底层原理,是提升系统设计能力和解决复杂工程问题的关键。例如,HashMap的扩容机制不仅涉及数据结构优化,更关联到并发安全与JVM内存模型;而秒杀场景的设计则综合运用了并发控制、数据库事务、缓存策略和分布式系统知识,体现了技术在实际业务中的价值。本文聚焦Java技术栈,针对校招面试中的高频考点,提供一套以问题为导向、串联核心知识的突击方法,帮助求职者高效构建知识防御体系,从容应对

weixin_30727835的博客 427

从信号量到实战:深入理解进程同步原理与并发编程实践

进程同步是操作系统与并发编程的核心概念,旨在解决多线程或多进程环境下的资源竞争与协作问题。其本质是通过互斥与同步机制,确保共享资源访问的一致性与正确性,避免竞态条件导致的数据混乱。从技术原理上看,信号量作为基础的同步原语,通过原子性的P/V操作实现对资源计数的管理,而互斥锁与条件变量则构成了更高级的管程模型,广泛应用于现代编程语言中。理解进程同步的技术价值在于,它是构建可靠、高效并发系统的基石,无论是操作系统内核、数据库管理系统,还是分布式应用,都离不开对同步机制的恰当运用。在实际应用场景中,从经典的生产者

weixin_34195546的博客 395

Java高并发系统 + 安全监控 完整知识体系

本文系统梳理了Java高并发开发的全方位知识体系,涵盖单机并发基础、分布式架构、系统安全、监控体系和故障治理等核心模块。主要内容包括: 单机并发基础 CPU多级缓存与内存模型 JMM内存模型与happens-before原则 锁机制与CAS原子操作 并发容器与线程池原理 JVM调优与性能优化 分布式高并发架构 分布式锁与一致性算法 缓存体系与典型问题解决方案 消息队列的削峰填谷 负载均衡与分库分表 微服务通信与流量控制 生产级解决方案 高并发业务安全防护 全链路监控体系搭建 典型故障排查与治理 应急预案与性

专注 Java 后端开发,分享基础语法、JVM、并发、Spring 全家桶、微服务、分布式、性能优化等实战干货。坚持输出高质量技术笔记,用简单语言讲清复杂原理,助力每一位 Java 开发者稳步进阶。 575

Java面试85题图解版 · 特别篇:2026后端高频面试题复盘(算法底层逻辑+高并发架构设计全解析,附Java实战代码)

基于2026春招/实习面经,精选10+道后端高频核心题,涵盖双指针、动态规划、回溯剪枝、滑动窗口、环形链表、LRU缓存、环形打家劫舍、SQL原地操作、B+树索引、KMP字符串匹配、HTTP演进与TCP调优。每道题附带完整可运行Java代码、面试官追问、进阶思考。末尾补充系统设计REACT推导法,助你打通技术与业务的底层逻辑。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 555

Java线程6大生命周期完整解析:状态转换、代码演示、面试必考

Java 的线程状态被明确定义在这个内部枚举类中,从 JDK1.5 引入后,该枚举的结构一直稳定延续到 JDK17 及后续版本。// 由JVM维护的线程真实状态值// 线程状态枚举定义/\*\* 新建状态:线程对象已创建,但尚未调用start()方法 \*/NEW,/\*\* 可运行状态:调用start()方法后,线程处于可执行状态,等待CPU调度或正在执行 \*/RUNNABLE,

网络工程师|十多年企业级运维实战|精通 ‌BGP、OSPF、VLAN、SDN、MPLS、ACL、NetFlow、SNMP、 384

futex同步机制分析之二glibc的实现

在前方中提到过,经常使用的几个线程(进程)同步机制,其实都是调用futex来实现的,下面就从glibc2.8中把源码找出来对一下,看看到底是不是确实是如此,同时,对代码进行一些简单的分析。  每个细节的实现其实都可以展开来讲成独立的一篇,这里只是介绍同步机制的来龙去脉和实现,就不再对具体的细节进行展开,回头如果有时间,再对具体的每个技术进行专门的分析。  一、信号灯的实现 int __n...

fpcc的专栏 2486

Jedis各api的用法总结

下面是jedis工具类的各个方法的使用简略说明,主要给自己做一个备忘 Boolean isAlive(); Long del(String var1); Long del(String... var1); DataType type(String var1); 返回要查询的key的类型 Boolean expire(String var1, lo...

半岛落枫的博客 1091
上一篇: 【Java】泛型的理解与使用,包装类
下一篇: 神操!精选JVM垃圾回收机制全面分析,聊聊你眼中的JVM
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值