当前位置: 首页 > news >正文

Java Lock LockSupport 源码

前言


 相关系列

  • 《Java & Lock & 目录》(持续更新)
  • 《Java & Lock & LockSupport & 源码》(学习过程/多有漏误/仅作参考/不再更新)
  • 《Java & Lock & LockSupport & 总结》(学习总结/最新最准/持续更新)
  • 《Java & Lock & LockSupport & 问题》(学习解答/持续更新)
     
     

源码


/** ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.*********************//******* Written by Doug Lea with assistance from members of JCP JSR-166* Expert Group and released to the public domain, as explained at* http://creativecommons.org/publicdomain/zero/1.0/*/package juc.locks;import juc.Semaphore;
import sun.misc.Unsafe;import java.util.concurrent.ThreadLocalRandom;/*** Basic thread blocking primitives for creating locks and other synchronization classes.* 用于创建锁和其它同步类的基本线程阻塞原语。* <p>* This class associates, with each thread that uses it, a permit (in the sense of the* {@link Semaphore Semaphore} class). A call to {@code park} will return immediately if the permit is available,* consuming it in the process; otherwise it <em>may</em> block.  A call to {@code unpark} makes the permit available,* if it was not already available. (Unlike with Semaphores though, permits do not accumulate. There is at most one.)* 该类与每个使用它的线程关联一个许可(从信号量类的角度上看)。如果许可可用则调用一次park()方法将立即返回,并* 在过程中消费它;否则它将可能阻塞。如果许可不可用,则调用一次unpark()方法可使得其可用。(但是不像信号量,许* 可无法累积。最多只能有一个。)* <p>* Methods {@code park} and {@code unpark} provide efficient means of blocking and unblocking threads that do not* encounter the problems that cause the deprecated methods {@code Thread.suspend} and {@code Thread.resume} to* beunusable for such purposes: Races between one thread invoking {@code park} and another thread trying to* {@code unpark} it will preserve liveness, due to the permit. Additionally, {@code park} will return if the caller's* threadwas interrupted, and timeout versions are supported. The {@code park} method may also return at any other* time, for "no reason", so in general must be invoked within a loop that rechecks conditions upon return. In this sense* {@code park}serves as an optimization of a "busy wait" that does not waste as much time spinning, but must be* paired with an {@code unpark} to be effective.* 方法park和unpark提供了阻塞和解除阻塞线程的有效方法,这些方法不会遇到导致已弃用方法suspend和resume对于如此* 目的无用的问题:在线程调用park方法与其它线程尝试unpark它之间竞争将由于许可而保持活性。此外,如果调用者的线* 程已被中断,以及支持超时版本则park将返回。park方法也可能在任意其它时间无理由的返回,所以通常必须在返回后检* 查条件的循环中被调用。从这个角度上看park方法作为忙等待的优化手段不会浪费太多自旋时间,但必须与一个unpark方* 法成对使用以令之有效。* <p>* The three forms of {@code park} each also support a {@code blocker} object parameter. This object is recorded while* the thread is blocked to permit monitoring and diagnostic tools to identify the reasons that threads are blocked. (Such* tools may access blockers using method {@link #getBlocker(Thread)}.) The use of these forms rather than the original* forms without this parameter is strongly encouraged. The normal argument to supply as a {@code blocker} within a* lock implementation is {@code this}.* park方法的三种格式每个都支持一个阻塞者对象参数。该对象在线程在线程阻塞期间被记录,以允许监视器和诊断工具识* 别线程阻塞的原因。(该工具可能访问使用getBlocker(Thread)方法访问阻塞者)。强烈鼓励使用这些格式而不是原本没有* 参数的原始格式。该常规参数用于在一个锁实现中供应一个阻塞者。* <p>* These methods are designed to be used as tools for creating higher-level synchronization utilities, and are not in* themselves useful for most concurrency control applications.  The {@code park} method is designed for use only in* constructions of the form:* 这些方法被设计用于作为创建更高等级的同步程序的工具,并且它们本身对于大多数并发控制应用程序并没有用(意思是* 不会在API中使用相应的方法保证线程安全,而是使用基于它们创建的线程安全工具保证线程安全)。park方法被设计只在* 以下格式中构造:* <pre> {@code* while (!canProceed()) { ... LockSupport.park(this); }}</pre>* <p>* where neither {@code canProceed} nor any other actions prior to the call to {@code park} entail locking or blocking.* Because only one permit is associated with each thread, any intermediary uses of {@code park} could interfere with* its intended effects.* 其中即不是canProceed方法也不是任意其它优先于park方法的活动都需要加锁或阻塞(意思是循环中位于park方法之后的* 代码都必须是线程安全的...妈的什么鬼英语)。因为线程只关联一个许可,因此任意park方法的中间调用都会干涉它的预* 期效果(简而言之就是要要避免外部干扰)。** <p>* <b>Sample Usage.</b> Here is a sketch of a first-in-first-out non-reentrant lock class:* 简单用法。这是一个先入先出非重入锁类的草图:* <pre> {@code* class FIFOMutex {*   // ---- 原子布尔,作为锁使用。*   private final AtomicBoolean locked = new AtomicBoolean(false);*   // ---- 等待者队列。*   private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();**   // ---- 加锁。*   public void lock() {*     // ---- 经线程加入等待者集中。*     boolean wasInterrupted = false;*     Thread current = Thread.currentThread();*     waiters.add(current);**     // Block while not first in queue or cannot acquire lock*     // 在非队列首个线程或无法获取锁的情况下阻塞**     while (waiters.peek() != current || !locked.compareAndSet(false, true)) {*       LockSupport.park(this);*       if (Thread.interrupted())*         // ignore interrupts while waiting*         // 忽略在等待期间的中断(即该方法在设计上是不允许被中断的)*         wasInterrupted = true;*     }*     // ---- 退出循环后,将当前线程从等待者集中移除。*     waiters.remove();*     // reassert interrupt status on exit*     // 在退出时重设中断状态。*     if (wasInterrupted)*       current.interrupt();*   }**   public void unlock() {*     locked.set(false);*     // ---- 唤醒等待者集中的首个线程。*     LockSupport.unpark(waiters.peek());*   }* }}</pre>*/
public class LockSupport {/*** @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* ----* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 初始化锁支持者* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ----*/private LockSupport() {// Cannot be instantiated.// 无法(外部)实例化}/*** @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 设置阻塞者* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 为指定线程设置指定阻塞者* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法直接通过CAS操作完成。*/private static void setBlocker(Thread t, Object arg) {// Even though volatile, hotspot doesn't need a write barrier here.// 虽然易变,但热点在这里不需要一个写屏障。UNSAFE.putObject(t, parkBlockerOffset, arg);}/*** Makes available the permit for the given thread, if it was not already available.  If the thread was blocked on* {@code park} then it will unblock.  Otherwise, its next call to {@code park} is guaranteed not to block. This* operation is not guaranteed to have any effect at all if the given thread has not been started.* 如果指定线程尚未可用,则令其许可可用。如果线程它因为park方法而阻塞则它将解除阻塞。否则,它的下次park方* 法调用将不保证阻塞。如果指定线程尚未启动则该操作无法保证在任意情况下都有效果。** @param thread the thread to unpark, or {@code null}, in which case this operation has no effect*               用于释放的线程,或者为null,在该情况下该操作没有效果* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 解除停放* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 为指定线程分配一个许可,如果指定线程因为许可负债(-1)而处于等待状态,则将因为负债被清零而恢复运行;如果* 指定线程因为许可平衡(0)而处于运行状态,则将因为许可结余(1)而不会在下一次许可消耗中进入等待状态。如果* 执行线程尚未启动则上述所有情况都不保证一定会发生。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法直接通过CAS机制实现。*/public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}/*** Disables the current thread for thread scheduling purposes unless the permit is available.* 出于线程调度目的令当前线程无效,除非许可可用。* <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of three things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下三种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态。** @param blocker the synchronization object responsible for this thread parking*                该同步对象负责当前线程的停泊* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入无限等待状态;直至因为信号、中断及虚假的原* 因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法首先会为指定线程设置阻塞者,随后令之进入等待状态,如果被唤醒或进入等待状态失败则将阻塞者清除并返* 回。阻塞者的作用是令唤醒当前线程可以判断是否满足条件,功能是自定义的。* @since 1.6*/public static void park(Object blocker) {// ---- 方法首先会为指定线程设置阻塞者,随后令之进入等待状态,如果被唤醒或进入等待状态失败则将阻塞者清除// 并返回。阻塞者的作用是令唤醒当前线程可以判断是否满足条件,功能是自定义的。Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, 0L);setBlocker(t, null);}/*** Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit* is available.* 出于调度线程的目的令当前线程无效,直至指定等待时间,除非许可可用。* <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of four things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下四种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The specified waiting time elapses; or* 指定等待时间消逝;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* </ul>** <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread, or the elapsed time upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态,或过期时间。** @param blocker the synchronization object responsible for this thread parking*                该同步对象负责当前线程的停泊* @param nanos   the maximum number of nanoseconds to wait*                等待的最大纳秒时间* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊纳秒* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入有限等待状态;直至因为信号、中断、超时及虚* 假的原因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法首先会判断指定等待时间是否合法,合法则为指定线程设置阻塞者,随后令之进入等待状态,如果被唤醒或进* 入等待状态失败则将阻塞者清除并返回。* @since 1.6*/public static void parkNanos(Object blocker, long nanos) {if (nanos > 0) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(false, nanos);setBlocker(t, null);}}/*** Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available.* 出于调度线程的目的令当前线程无效,直至指定死亡线,除非许可可用。* <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of four things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下四种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The specified waiting time elapses; or* 指定等待时间消逝;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* </ul>* <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread, or the current time upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态,或过期时间。** @param blocker  the synchronization object responsible for this thread parking*                 该同步对象负责当前线程的停泊* @param deadline the absolute time, in milliseconds from the Epoch, to wait until*                 从纪元到等待的绝对时间,以毫秒为单位* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊直至* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入有限等待状态;直至因为信号、中断、超时及虚* 假的原因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法首先会为指定线程设置阻塞者,随后令之进入等待状态,如果被唤醒或进入等待状态失败则将阻塞者清除并返* 回。* @since 1.6*/public static void parkUntil(Object blocker, long deadline) {Thread t = Thread.currentThread();setBlocker(t, blocker);UNSAFE.park(true, deadline);setBlocker(t, null);}/*** Returns the blocker object supplied to the most recent invocation of a park method that has not yet unblocked, or* null if not blocked.  The value returned is just a momentary snapshot -- the thread may have since unblocked or* blocked on a different blocker object.* 返回由最近一次还未解除阻塞的park方法调用提供的阻塞者对象,或者如果未阻塞则为null。返回的值只是一个短暂的* 快照-- 该线程可能已解除阻塞或在另一个阻塞对象中阻塞。** @param t the thread 线程* @return the blocker 阻塞者* @throws NullPointerException if argument is null*                              空指针异常:如果参数为null* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 获取阻塞者* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 获取当前线程的阻塞者。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ----* @since 1.6*/public static Object getBlocker(Thread t) {if (t == null)throw new NullPointerException();return UNSAFE.getObjectVolatile(t, parkBlockerOffset);}/*** Disables the current thread for thread scheduling purposes unless the permit is available.* 出于线程调度目的令当前线程无效,除非许可可用。* <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of three things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下三种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* </ul>** <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态。** @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入无限等待状态;直至因为信号、中断及虚假的原* 因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ----*/public static void park() {UNSAFE.park(false, 0L);}/*** Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit* is available.* 出于调度线程的目的令当前线程无效,直至指定等待时间,除非许可可用。** <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of four things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下四种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The specified waiting time elapses; or* 指定等待时间消逝;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* </ul>** <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread, or the elapsed time upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态,或过期时间。** @param nanos the maximum number of nanoseconds to wait*              等待的最大纳秒时间* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊纳秒* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入有限等待状态;直至因为信号、中断、超时及虚* 假的原因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ----*/public static void parkNanos(long nanos) {if (nanos > 0)UNSAFE.park(false, nanos);}/*** Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available.* 出于调度线程的目的令当前线程无效,直至指定死亡线,除非许可可用。* <p>* If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes* disabled for thread scheduling purposes and lies dormant until one of four things happens:* 如果许可可用则消费许可并立即返回;否则当前线程处于线程调度目的将变得无效,并且休眠至以下四种情况发生:* <ul>* <li>Some other thread invokes {@link #unpark unpark} with the current thread as the target; or* 某些其它线程将当前线程作为目标调用unpark方法;或者* <li>Some other thread {@linkplain Thread#interrupt interrupts} the current thread; or* 某些其它线程中断当前线程;或者* <li>The specified waiting time elapses; or* 指定等待时间消逝;或者* <li>The call spuriously (that is, for no reason) returns. </ul>* 该调用虚假地(无理由地)返回。* </ul>* <p>* This method does <em>not</em> report which of these caused the method to return. Callers should re-check the* conditions which caused the thread to park in the first place. Callers may also determine, for example, the* interrupt status of the thread, or the current time upon return.* 该方法不会记录什么导致方法返回。调用者首先应该重检查导致线程停泊的条件(是否满足)。调用者可能还要确定,* 例如,线程返回后的中断状态,或过期时间。** @param deadline the absolute time, in milliseconds from the Epoch, to wait until*                 从纪元到等待的绝对时间,以毫秒为单位* @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 停泊直至* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 消耗指定线程的一个许可,消耗后如果指定线程许可负债(-1)则进入有限等待状态;直至因为信号、中断、超时及虚* 假的原因而唤醒;否则直接返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法首先会为指定线程设置阻塞者,随后令之进入等待状态,如果被唤醒或进入等待状态失败则将阻塞者清除并返* 回。*/public static void parkUntil(long deadline) {UNSAFE.park(true, deadline);}/*** Returns the pseudo-randomly initialized or updated secondary seed. Copied from ThreadLocalRandom due to package* access restrictions.* 虚假随机地返回初始化的或更新的次要个种子。由于包访问限制从ThreadLocalRandom中赋值。** @Description: ----------------------------------------------------------- 名称 -----------------------------------------------------------* 下个次要种子* @Description: ----------------------------------------------------------- 作用 -----------------------------------------------------------* 基于当前线程的次要种子生成新的次要种子并保存/返回。* @Description: ----------------------------------------------------------- 逻辑 -----------------------------------------------------------* ---- 方法首先会从当前线程中获取次要种子并判断是否为0,为0表示为初始值,为之随机分配一个值。但为了防止随机* 的数值为0需要在该情况下手动将之赋值为1;如果不为0则说明次要种子已经被更新过,基于其进行位运算已生成新的* 次要种子。* ---- 新的次要种子生成后将之重新保存至当前线程中并返回。*/static final int nextSecondarySeed() {int r;Thread t = Thread.currentThread();if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {// ---- 从线程中获取次要种子,如果不为0,说明已经经历过更新,直接进行位操作。r ^= r << 13;   // xorshiftr ^= r >>> 17;r ^= r << 5;} else if ((r = ThreadLocalRandom.current().nextInt()) == 0)// avoid zero// 避免0// ---- 如果次要种子为0,说明为初始值,尚未经历过更新,因此为之分配一个随机数。但是为了防止其再次为0需// 要在其位0是手动赋值为1。r = 1;// ---- 新的次要种子被分配后,重新保存。UNSAFE.putInt(t, SECONDARY, r);return r;}// Hotspot implementation via intrinsics APIprivate static final Unsafe UNSAFE;private static final long parkBlockerOffset;private static final long SEED;private static final long PROBE;private static final long SECONDARY;static {try {UNSAFE = Unsafe.getUnsafe();Class<?> tk = Thread.class;parkBlockerOffset = UNSAFE.objectFieldOffset(tk.getDeclaredField("parkBlocker"));SEED = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSeed"));PROBE = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomProbe"));SECONDARY = UNSAFE.objectFieldOffset(tk.getDeclaredField("threadLocalRandomSecondarySeed"));} catch (Exception ex) {throw new Error(ex);}}}

http://www.mrgr.cn/news/61349.html

相关文章:

  • .net core 中使用AsyncLocal传递变量
  • 第 5 场 算法季度赛
  • Elasticsearch ES|QL 地理空间索引加入纽约犯罪地图
  • JAVA多线程学习
  • 蓝桥杯备考:数据结构之栈 和 stack
  • 全网首发:编译libssh,产生类似undefined reference to `EVP_aes_256_ctr@OPENSSL_1_1_0‘的大量错误
  • 代码学习:如何阅读开源代码
  • 网络搜索引擎Shodan(6)
  • 今日总结10.29
  • 重学SpringBoot3-怎样优雅停机
  • 基于ssm+jsp639实验室排课系统设计与实现
  • 力扣每日一题 3211. 生成不含相邻零的二进制字符串
  • 代码随想录 | Day35 | 动态规划 :最小花费爬楼梯不同路径不同路径II
  • Spring Cloud 和 Dubbo 的区别
  • 超好玩又简单-猜数字游戏(有手就行)
  • 【JavaEE】【多线程】定时器
  • 《机器学习by周志华》学习笔记-神经网络-03全局最小误差与局部极小误差
  • QT中使用图表之QChart绘制曲线图
  • Sqoop的安装配置及使用
  • Coredump-A: 配置相关:suid_dumpable
  • 大数据新视界 -- 大数据大厂之大数据重塑影视娱乐产业的未来(4 - 3)
  • 深度学习:Overfitting 成因及解决策略
  • Diving into the HAL-----Interrupts
  • AutoDIR: Automatic All-in-One Image Restoration with Latent Diffusion论文阅读笔记
  • 线上Bug排查清单,测试小哥拿走不谢!
  • Docker快速安装Grafana