JavaSE-多线程(5.2)- ReentrantLock (源码解析,公平模式)

Ansys Maxwell磁场仿真入门:无线传能线圈自感与互感计算全流程 本文详细介绍了使用Ansys Maxwell进行无线充电线圈磁场仿真的全流程,包括模型建立、求解器设置、电感参数提取及优化策略。通过实战案例,帮助工程师掌握线圈自感与互感计算技巧,提升无线充电系统设计效率与精度。重点解析了Maxwell在磁场仿真中的关键应用,适用于电磁场仿真初学者和进阶用户。 阅读详情

JavaSE-多线程(5.2)- ReentrantLock (源码解析,公平模式)

ReentrantLock 解释

ReentrantLock 是 java JUC 包(java.util.concurrent)下的可重入锁工具类,具体功能实现依赖AbstractQueuedSynchronizer(AQS,抽象队列同步器),AQS 内部维护了一个变量 private volatile int state; 默认为 0,当线程上锁以后值变成 1 (state 值改变通过 CAS 实现),没有获取到锁的线程会加入等待对列。

ReentrantLock 使用举例

通过一个例子来分析 ReentrantLock 的执行原理:

public class FairLockAndNonFairLockTest {

    static class Task implements Runnable {
        private final ReentrantLock lock;
        private volatile int count = 0;

        public Task(boolean fair) {
            this.lock = new ReentrantLock(fair);
        }

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                doTask();
            }
        }

        private void doTask() {
            lock.lock();
            try {
                System.out.println("任务开始。。。, 线程:" + Thread.currentThread().getName() + " , count=" + count);
                count++;
                sleep(500);
                System.out.println("任务结束。。。, 线程:" + Thread.currentThread().getName() + " , count=" + count);
                printQueuedThreads(lock);
            } finally {
                lock.unlock();
            }
        }
    }

    public static void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static void printQueuedThreads(ReentrantLock lock) {
        Class<ReentrantLock> reentrantLockClass = (Class<ReentrantLock>) lock.getClass();
        try {
            // getQueuedThreads 方法访问权限是 protected ,所以通过反射方式访问
            Method getQueuedThreads = reentrantLockClass.getDeclaredMethod("getQueuedThreads");
            getQueuedThreads.setAccessible(true);
            // 获取 aqs 中的线程队列
            List<Thread> list = (List<Thread>) getQueuedThreads.invoke(lock);
            // 看 getQueuedThreads 方法源码可知,它的顺序是从tail开始 加入list的,这里给它倒过来,从 head 开始
            Collections.reverse(list);
            System.out.println(list);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        Task task = new Task(true);
        for (int i = 0; i < 5; i++) {
            new Thread(task, "t" + (i + 1)).start();

            // 稍微延迟启动,让线程按顺序创建和启动
            sleep(10);
        }
    }
}

以上代码中启动了5个线程,它们各自都会执行task任务,每个task任务会循环3次调用 doTask 方法,doTask方法有加锁操作,每执行一次 count 加1,并且在doTask方法中会打印当前等待队列中的线程。
查看运行结果(是否和你预期一致?):

任务开始。。。, 线程:t1 , count=0
任务结束。。。, 线程:t1 , count=1
[Thread[t2,5,main], Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main]]
任务开始。。。, 线程:t2 , count=1
任务结束。。。, 线程:t2 , count=2
[Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main], Thread[t1,5,main]]
任务开始。。。, 线程:t3 , count=2
任务结束。。。, 线程:t3 , count=3
[Thread[t4,5,main], Thread[t5,5,main], Thread[t1,5,main], Thread[t2,5,main]]
任务开始。。。, 线程:t4 , count=3
任务结束。。。, 线程:t4 , count=4
[Thread[t5,5,main], Thread[t1,5,main], Thread[t2,5,main], Thread[t3,5,main]]
任务开始。。。, 线程:t5 , count=4
任务结束。。。, 线程:t5 , count=5
[Thread[t1,5,main], Thread[t2,5,main], Thread[t3,5,main], Thread[t4,5,main]]
任务开始。。。, 线程:t1 , count=5
任务结束。。。, 线程:t1 , count=6
[Thread[t2,5,main], Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main]]
任务开始。。。, 线程:t2 , count=6
任务结束。。。, 线程:t2 , count=7
[Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main], Thread[t1,5,main]]
任务开始。。。, 线程:t3 , count=7
任务结束。。。, 线程:t3 , count=8
[Thread[t4,5,main], Thread[t5,5,main], Thread[t1,5,main], Thread[t2,5,main]]
任务开始。。。, 线程:t4 , count=8
任务结束。。。, 线程:t4 , count=9
[Thread[t5,5,main], Thread[t1,5,main], Thread[t2,5,main], Thread[t3,5,main]]
任务开始。。。, 线程:t5 , count=9
任务结束。。。, 线程:t5 , count=10
[Thread[t1,5,main], Thread[t2,5,main], Thread[t3,5,main], Thread[t4,5,main]]
任务开始。。。, 线程:t1 , count=10
任务结束。。。, 线程:t1 , count=11
[Thread[t2,5,main], Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main]]
任务开始。。。, 线程:t2 , count=11
任务结束。。。, 线程:t2 , count=12
[Thread[t3,5,main], Thread[t4,5,main], Thread[t5,5,main]]
任务开始。。。, 线程:t3 , count=12
任务结束。。。, 线程:t3 , count=13
[Thread[t4,5,main], Thread[t5,5,main]]
任务开始。。。, 线程:t4 , count=13
任务结束。。。, 线程:t4 , count=14
[Thread[t5,5,main]]
任务开始。。。, 线程:t5 , count=14
任务结束。。。, 线程:t5 , count=15
[]

以上,由于线程启动后 sleep(10); 所以总是线程 t1 先执行,其他线程由于竞争不到锁会依次加入到等待队列中,当线程 t1 执行完 1 次后,t1释放锁,t1 会继续循环,但是由于是公平模式,所以 t1 会加入到等待队列,t2 成功获取锁继续执行,依次交替执行。

ReentrantLock 源码解析

ReentrantLock 源码如下:

package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;

/**
 * @since 1.5
 * @author Doug Lea
 */
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** 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 {
        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();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                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;
        }

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    /**
     * Sync object for non-fair locks
     */
    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(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * 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;
        }
    }

    /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

    /**
     * Acquires the lock.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately, setting the lock hold count to one.
     *
     * <p>If the current thread already holds the lock then the hold
     * count is incremented by one and the method returns immediately.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until the lock has been acquired,
     * at which time the lock hold count is set to one.
     */
    public void lock() {
        sync.lock();
    }

    /**
     * Acquires the lock unless the current thread is
     * {@linkplain Thread#interrupt interrupted}.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately, setting the lock hold count to one.
     *
     * <p>If the current thread already holds this lock then the hold count
     * is incremented by one and the method returns immediately.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until one of two things happens:
     *
     * <ul>
     *
     * <li>The lock is acquired by the current thread; or
     *
     * <li>Some other thread {@linkplain Thread#interrupt interrupts} the
     * current thread.
     *
     * </ul>
     *
     * <p>If the lock is acquired by the current thread then the lock hold
     * count is set to one.
     *
     * <p>If the current thread:
     *
     * <ul>
     *
     * <li>has its interrupted status set on entry to this method; or
     *
     * <li>is {@linkplain Thread#interrupt interrupted} while acquiring
     * the lock,
     *
     * </ul>
     *
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * <p>In this implementation, as this method is an explicit
     * interruption point, preference is given to responding to the
     * interrupt over normal or reentrant acquisition of the lock.
     *
     * @throws InterruptedException if the current thread is interrupted
     */
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    /**
     * Acquires the lock only if it is not held by another thread at the time
     * of invocation.
     *
     * <p>Acquires the lock if it is not held by another thread and
     * returns immediately with the value {@code true}, setting the
     * lock hold count to one. Even when this lock has been set to use a
     * fair ordering policy, a call to {@code tryLock()} <em>will</em>
     * immediately acquire the lock if it is available, whether or not
     * other threads are currently waiting for the lock.
     * This &quot;barging&quot; behavior can be useful in certain
     * circumstances, even though it breaks fairness. If you want to honor
     * the fairness setting for this lock, then use
     * {@link #tryLock(long, TimeUnit) tryLock(0, TimeUnit.SECONDS) }
     * which is almost equivalent (it also detects interruption).
     *
     * <p>If the current thread already holds this lock then the hold
     * count is incremented by one and the method returns {@code true}.
     *
     * <p>If the lock is held by another thread then this method will return
     * immediately with the value {@code false}.
     *
     * @return {@code true} if the lock was free and was acquired by the
     *         current thread, or the lock was already held by the current
     *         thread; and {@code false} otherwise
     */
    public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }

    /**
     * Acquires the lock if it is not held by another thread within the given
     * waiting time and the current thread has not been
     * {@linkplain Thread#interrupt interrupted}.
     *
     * <p>Acquires the lock if it is not held by another thread and returns
     * immediately with the value {@code true}, setting the lock hold count
     * to one. If this lock has been set to use a fair ordering policy then
     * an available lock <em>will not</em> be acquired if any other threads
     * are waiting for the lock. This is in contrast to the {@link #tryLock()}
     * method. If you want a timed {@code tryLock} that does permit barging on
     * a fair lock then combine the timed and un-timed forms together:
     *
     *  <pre> {@code
     * if (lock.tryLock() ||
     *     lock.tryLock(timeout, unit)) {
     *   ...
     * }}</pre>
     *
     * <p>If the current thread
     * already holds this lock then the hold count is incremented by one and
     * the method returns {@code true}.
     *
     * <p>If the lock is held by another thread then the
     * current thread becomes disabled for thread scheduling
     * purposes and lies dormant until one of three things happens:
     *
     * <ul>
     *
     * <li>The lock is acquired by the current thread; or
     *
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread; or
     *
     * <li>The specified waiting time elapses
     *
     * </ul>
     *
     * <p>If the lock is acquired then the value {@code true} is returned and
     * the lock hold count is set to one.
     *
     * <p>If the current thread:
     *
     * <ul>
     *
     * <li>has its interrupted status set on entry to this method; or
     *
     * <li>is {@linkplain Thread#interrupt interrupted} while
     * acquiring the lock,
     *
     * </ul>
     * then {@link InterruptedException} is thrown and the current thread's
     * interrupted status is cleared.
     *
     * <p>If the specified waiting time elapses then the value {@code false}
     * is returned.  If the time is less than or equal to zero, the method
     * will not wait at all.
     *
     * <p>In this implementation, as this method is an explicit
     * interruption point, preference is given to responding to the
     * interrupt over normal or reentrant acquisition of the lock, and
     * over reporting the elapse of the waiting time.
     *
     * @param timeout the time to wait for the lock
     * @param unit the time unit of the timeout argument
     * @return {@code true} if the lock was free and was acquired by the
     *         current thread, or the lock was already held by the current
     *         thread; and {@code false} if the waiting time elapsed before
     *         the lock could be acquired
     * @throws InterruptedException if the current thread is interrupted
     * @throws NullPointerException if the time unit is null
     */
    public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    }

    /**
     * Attempts to release this lock.
     *
     * <p>If the current thread is the holder of this lock then the hold
     * count is decremented.  If the hold count is now zero then the lock
     * is released.  If the current thread is not the holder of this
     * lock then {@link IllegalMonitorStateException} is thrown.
     *
     * @throws IllegalMonitorStateException if the current thread does not
     *         hold this lock
     */
    public void unlock() {
        sync.release(1);
    }

    /**
     * Returns a {@link Condition} instance for use with this
     * {@link Lock} instance.
     *
     * <p>The returned {@link Condition} instance supports the same
     * usages as do the {@link Object} monitor methods ({@link
     * Object#wait() wait}, {@link Object#notify notify}, and {@link
     * Object#notifyAll notifyAll}) when used with the built-in
     * monitor lock.
     *
     * <ul>
     *
     * <li>If this lock is not held when any of the {@link Condition}
     * {@linkplain Condition#await() waiting} or {@linkplain
     * Condition#signal signalling} methods are called, then an {@link
     * IllegalMonitorStateException} is thrown.
     *
     * <li>When the condition {@linkplain Condition#await() waiting}
     * methods are called the lock is released and, before they
     * return, the lock is reacquired and the lock hold count restored
     * to what it was when the method was called.
     *
     * <li>If a thread is {@linkplain Thread#interrupt interrupted}
     * while waiting then the wait will terminate, an {@link
     * InterruptedException} will be thrown, and the thread's
     * interrupted status will be cleared.
     *
     * <li> Waiting threads are signalled in FIFO order.
     *
     * <li>The ordering of lock reacquisition for threads returning
     * from waiting methods is the same as for threads initially
     * acquiring the lock, which is in the default case not specified,
     * but for <em>fair</em> locks favors those threads that have been
     * waiting the longest.
     *
     * </ul>
     *
     * @return the Condition object
     */
    public Condition newCondition() {
        return sync.newCondition();
    }

    /**
     * Queries the number of holds on this lock by the current thread.
     *
     * <p>A thread has a hold on a lock for each lock action that is not
     * matched by an unlock action.
     *
     * <p>The hold count information is typically only used for testing and
     * debugging purposes. For example, if a certain section of code should
     * not be entered with the lock already held then we can assert that
     * fact:
     *
     *  <pre> {@code
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *   public void m() {
     *     assert lock.getHoldCount() == 0;
     *     lock.lock();
     *     try {
     *       // ... method body
     *     } finally {
     *       lock.unlock();
     *     }
     *   }
     * }}</pre>
     *
     * @return the number of holds on this lock by the current thread,
     *         or zero if this lock is not held by the current thread
     */
    public int getHoldCount() {
        return sync.getHoldCount();
    }

    /**
     * Queries if this lock is held by the current thread.
     *
     * <p>Analogous to the {@link Thread#holdsLock(Object)} method for
     * built-in monitor locks, this method is typically used for
     * debugging and testing. For example, a method that should only be
     * called while a lock is held can assert that this is the case:
     *
     *  <pre> {@code
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *
     *   public void m() {
     *       assert lock.isHeldByCurrentThread();
     *       // ... method body
     *   }
     * }}</pre>
     *
     * <p>It can also be used to ensure that a reentrant lock is used
     * in a non-reentrant manner, for example:
     *
     *  <pre> {@code
     * class X {
     *   ReentrantLock lock = new ReentrantLock();
     *   // ...
     *
     *   public void m() {
     *       assert !lock.isHeldByCurrentThread();
     *       lock.lock();
     *       try {
     *           // ... method body
     *       } finally {
     *           lock.unlock();
     *       }
     *   }
     * }}</pre>
     *
     * @return {@code true} if current thread holds this lock and
     *         {@code false} otherwise
     */
    public boolean isHeldByCurrentThread() {
        return sync.isHeldExclusively();
    }

    /**
     * Queries if this lock is held by any thread. This method is
     * designed for use in monitoring of the system state,
     * not for synchronization control.
     *
     * @return {@code true} if any thread holds this lock and
     *         {@code false} otherwise
     */
    public boolean isLocked() {
        return sync.isLocked();
    }

    /**
     * Returns {@code true} if this lock has fairness set true.
     *
     * @return {@code true} if this lock has fairness set true
     */
    public final boolean isFair() {
        return sync instanceof FairSync;
    }

    /**
     * Returns the thread that currently owns this lock, or
     * {@code null} if not owned. When this method is called by a
     * thread that is not the owner, the return value reflects a
     * best-effort approximation of current lock status. For example,
     * the owner may be momentarily {@code null} even if there are
     * threads trying to acquire the lock but have not yet done so.
     * This method is designed to facilitate construction of
     * subclasses that provide more extensive lock monitoring
     * facilities.
     *
     * @return the owner, or {@code null} if not owned
     */
    protected Thread getOwner() {
        return sync.getOwner();
    }

    /**
     * Queries whether any threads are waiting to acquire this lock. Note that
     * because cancellations may occur at any time, a {@code true}
     * return does not guarantee that any other thread will ever
     * acquire this lock.  This method is designed primarily for use in
     * monitoring of the system state.
     *
     * @return {@code true} if there may be other threads waiting to
     *         acquire the lock
     */
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

    /**
     * Queries whether the given thread is waiting to acquire this
     * lock. Note that because cancellations may occur at any time, a
     * {@code true} return does not guarantee that this thread
     * will ever acquire this lock.  This method is designed primarily for use
     * in monitoring of the system state.
     *
     * @param thread the thread
     * @return {@code true} if the given thread is queued waiting for this lock
     * @throws NullPointerException if the thread is null
     */
    public final boolean hasQueuedThread(Thread thread) {
        return sync.isQueued(thread);
    }

    /**
     * Returns an estimate of the number of threads waiting to
     * acquire this lock.  The value is only an estimate because the number of
     * threads may change dynamically while this method traverses
     * internal data structures.  This method is designed for use in
     * monitoring of the system state, not for synchronization
     * control.
     *
     * @return the estimated number of threads waiting for this lock
     */
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

    /**
     * Returns a collection containing threads that may be waiting to
     * acquire this lock.  Because the actual set of threads may change
     * dynamically while constructing this result, the returned
     * collection is only a best-effort estimate.  The elements of the
     * returned collection are in no particular order.  This method is
     * designed to facilitate construction of subclasses that provide
     * more extensive monitoring facilities.
     *
     * @return the collection of threads
     */
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }

    /**
     * Queries whether any threads are waiting on the given condition
     * associated with this lock. Note that because timeouts and
     * interrupts may occur at any time, a {@code true} return does
     * not guarantee that a future {@code signal} will awaken any
     * threads.  This method is designed primarily for use in
     * monitoring of the system state.
     *
     * @param condition the condition
     * @return {@code true} if there are any waiting threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    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);
    }

    /**
     * Returns an estimate of the number of threads waiting on the
     * given condition associated with this lock. Note that because
     * timeouts and interrupts may occur at any time, the estimate
     * serves only as an upper bound on the actual number of waiters.
     * This method is designed for use in monitoring of the system
     * state, not for synchronization control.
     *
     * @param condition the condition
     * @return the estimated number of waiting threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    public int getWaitQueueLength(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * Returns a collection containing those threads that may be
     * waiting on the given condition associated with this lock.
     * Because the actual set of threads may change dynamically while
     * constructing this result, the returned collection is only a
     * best-effort estimate. The elements of the returned collection
     * are in no particular order.  This method is designed to
     * facilitate construction of subclasses that provide more
     * extensive condition monitoring facilities.
     *
     * @param condition the condition
     * @return the collection of threads
     * @throws IllegalMonitorStateException if this lock is not held
     * @throws IllegalArgumentException if the given condition is
     *         not associated with this lock
     * @throws NullPointerException if the condition is null
     */
    protected Collection<Thread> getWaitingThreads(Condition condition) {
        if (condition == null)
            throw new NullPointerException();
        if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
            throw new IllegalArgumentException("not owner");
        return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
    }

    /**
     * Returns a string identifying this lock, as well as its lock state.
     * The state, in brackets, includes either the String {@code "Unlocked"}
     * or the String {@code "Locked by"} followed by the
     * {@linkplain Thread#getName name} of the owning thread.
     *
     * @return a string identifying this lock, as well as its lock state
     */
    public String toString() {
        Thread o = sync.getOwner();
        return super.toString() + ((o == null) ?
                                   "[Unlocked]" :
                                   "[Locked by thread " + o.getName() + "]");
    }
}

ReentrantLock 类内有一个内部抽象类 Sync

abstract static class Sync extends AbstractQueuedSynchronizer

公平同步器 FairSync 和非公平同步器 NonfairSync 都是继承至 Sync,以公平同步器为例:

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;
    }
}

① 创建公平锁

如下代码表示创建公平锁,

private final ReentrantLock lock =  new ReentrantLock(true);

程序会执行如下代码:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

即将 FairSync() 对象赋值给 引用 private final Sync sync;

FairSync 类继承关系:

② 获取锁 lock.lock();

实际执行的是 FairSync 类的 lock 方法:

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

如下,当第一个线程t1启动时,会尝试获取锁,即进入 if (c == 0) 代码块,

protected final boolean tryAcquire(int acquires) {
   final Thread current = Thread.currentThread();
    // 获取当前 state 值,为0时才可以修改其值 
    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;
}

当线程t1第一次执行时,tail = null,head = null,所以该方法返回 false,

// 判断AbstractQueuedSynchronizer中有没有等待的队列
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

接着执行 compareAndSetState(0, acquires) 代码,即通过 CAS 将 state 状态由 0 改成 1 ,由于主线程睡眠了10毫秒,所以其他线程启动推迟,大概率由 t1 获取锁成功,所以此时 exclusiveOwnerThread = t1。

// AbstractOwnableSynchronizer  类方法,将当前获取到锁的线程赋值给 exclusiveOwnerThread 变量
protected final void setExclusiveOwnerThread(Thread thread) {
    exclusiveOwnerThread = thread;
}

② 其他尝试获取锁的线程加入等待队列

当线程没有获取到锁时,使用 Node 记录等待线程然后加入队列,Node 是 抽象类 AbstractQueuedSynchronizer 的内部类:

static final class Node {
    /** Marker to indicate a node is waiting in shared mode */
    static final Node SHARED = new Node();
    /** Marker to indicate a node is waiting in exclusive mode */
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled */
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;

    volatile int waitStatus;
    
    volatile Node prev;

    volatile Node next;

    /**
     * The thread that enqueued this node.  Initialized on
     * construction and nulled out after use.
     */
    volatile Thread thread;

    /**
     * Link to next node waiting on condition, or the special
     * value SHARED.  Because condition queues are accessed only
     * when holding in exclusive mode, we just need a simple
     * linked queue to hold nodes while they are waiting on
     * conditions. They are then transferred to the queue to
     * re-acquire. And because conditions can only be exclusive,
     * we save a field by using special value to indicate shared
     * mode.
     */
    Node nextWaiter;

    /**
     * Returns true if node is waiting in shared mode.
     */
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

线程 t1 获取锁成功后开始执行 run 方法循环体中的 doTask 方法,其他线程启动后也会调用 lock.lock(); 方法, 由于此时 state =1 (即锁被 t1 持有),所以 tryAcquire 返回 false,此时线程 t2 执行 acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 方法

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;
        }
    }
    enq(node);
    return node;
}

如下,t2 准备加入队列时,此时队列为空,首先会创建一个空的节点,head 和 tail 都指向这个空 node,

      				+------+   
 head 	====>		|  空  | 	 <====  tail        备注: <==== 和 ====> 表示指向,<----表示 prev ,----> 表示 next
      				+------+      

当执行一次循环后,队列里已经有了一个空节点,此时进入else逻辑,此时队列节点如下

      		+------+  <----  +-----+ 
 head ====> |  空  | 	     |  t2 | <==== tail
      		+------+  ---->  +-----+ 
private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        // 第一次执行时 t == null 
        if (t == null) { // Must initialize
            // 当队列为空时,会创建一个空的节点
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

当 t2 加入队列后执行如下方法,第一次循环时,t2 节点的上一个节点 p waitStatus = 0 ,shouldParkAfterFailedAcquire 方法将 p 节点waitStatus 改成 Node.SIGNAL(-1) 并且返回false,再次循环,shouldParkAfterFailedAcquire 方法返回true

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            // 获取 node 节点的上一个节点
            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);
    }
}

t2 执行 parkAndCheckInterrupt 方法,t2 线程进入 waiting 状态。

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

其他线程类似,由于竞争不到锁,同样会加入等待队列,如下:

      		+------+  <----  +-----+  <----  +-----+  <----  +-----+  <----  +-----+ 
 head ====> |  空  | 	     |  t2 | 	     |  t3 | 	     |  t4 | 	     |  t5 | <==== tail
      		+------+  ---->  +-----+  ---->  +-----+  ---->  +-----+  ---->  +-----+ 

③ 释放锁 lock.unlock();

public void unlock() {
  sync.release(1);
}

AbstractQueuedSynchronizer 类 release 方法

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

t1 释放锁时,c = 1 -1;进入 if (c == 0) ,将获取锁的线程 exclusiveOwnerThread 变量置null,并且将 state 改成 0,

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;
}

tryRelease 方法返回 true,执行 unparkSuccessor 方法:
第一步:将 t2 的前一个节点,即空节点 p 的 waitStatus 状态由 SIGNAL (-1) 改成 0 ,compareAndSetWaitStatus(node, ws, 0);
第二步:调用 LockSupport.unpark(s.thread); 将 t2 线程由 waiting 状态改成 runnable

private void unparkSuccessor(Node node) {
   /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

④ 其他线程获取锁 lock.lock();

此时 t1 和 t2 线程都处于 runnable 的状态,t1 执行下一次循环,重新尝试获取锁 lock.lock(); 但是由于调用 tryAcquire 方法时hasQueuedPredecessors 方法返回 true,主要是 s.thread != Thread.currentThread() ,因为此时头结点的下一个节点是 t2 ,此时线程是 t1 ,所以 线程 t1 会加入到等待队列。


而线程 t2 会从 if (shouldParkAfterFailedAcquire(p, node) && 继续执行,从新开始循环,执行 tryAcquire 方法成功,即 t2 获取锁成功。

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);
    }
}

所以新的队列是这样的:

      		+------+  <----  +-----+  <----  +-----+  <----  +-----+  <----  +-----+ 
 head ====> |  t2  | 	     |  t3 | 	     |  t4 | 	     |  t5 | 	     |  t1 | <==== tail
      		+------+  ---->  +-----+  ---->  +-----+  ---->  +-----+  ---->  +-----+ 

依次力推。。。。

手把手教你用FreeModbus RTU协议栈实现STM32与PLC通信(附Keil工程) 本文详细介绍了如何使用FreeModbus RTU协议栈在STM32上实现与PLC的工业通信。从环境搭建、工程配置到关键移植接口实现,提供了Keil工程模板和调试技巧,帮助开发者快速掌握Modbus RTU通信技术,适用于工业自动化领域的设备间通信需求。 阅读详情

相关推荐

APK静态分析(取证角度)——理论部分

APP是指应用程序(Application),是指能够在手机、平板电脑、电脑等移动设备上运行的软件。APP可以帮助用户进行各种功能操作,如社交媒体、游戏、购物、学习、工作等。用户可以通过应用商店或者在线下载安装APP,然后在设备上使用它们。APP的种类非常丰富,能够满足用户不同的需求和兴趣。

currify的博客 3205

JavaSE-多线程(5.3)- ReentrantLock (源码解析,非公平模式)

线程 t1 调用 lock.unlock();,但是线程 t2 不是立马进入running 状态,会从waitting 状态进入 ready 状态,待进入running状态后才继续执行循环体。由于此时 t1 线程已经是 running 状态,而 t2 是 ready 状态,所以 t1 优先获取到了锁,所以 t1 线程继续执行循环体,而 t2 线程进入 running 状态后 由于获取锁失败又会阻塞。程序运行结果与公平模式下对比,各个线程将各自任务执行完成后,才执行下一个线程的任务。

sql2008help的博客 253

告别手动标注!用ENVI Classic高效搞定高光谱图像语义分割标签(以农业/地物分类为例)

本文详细介绍了如何利用ENVI Classic实现高光谱图像的半自动化智能标注,特别针对农业和地物分类场景。通过光谱特征分析、智能标注技巧和MAT文件转换,显著提升标注效率和质量,助力AI模型训练。

weixin_28694169的博客 251

Java学习路径全解析:从语法到项目实战,构建扎实知识体系

Java作为企业级应用开发的主流语言,其核心在于构建扎实的知识体系和解决实际问题的能力。理解Java编程思想、掌握集合框架和并发编程原理,是提升开发效率和应对高并发场景的基础。集合框架如HashMap的底层实现、扩容机制,以及JUC包中的CAS、AQS等并发工具,是高性能应用开发的关键技术。这些技术广泛应用于电商、金融等需要处理大量数据和并发请求的场景。本文围绕JavaSE核心、面向对象设计、集合源码和JUC并发等热词,系统梳理了从基础语法到项目实战的学习路径,帮助开发者建立知识网络,避免常见学习陷阱,最终

weixin_34345753的博客 424

Hyper-V显卡直通实战:手把手解决Windows 11虚拟机错误码43(附完整参数配置)

本文详细解析了在Windows 11 Hyper-V虚拟机中配置显卡直通时遭遇错误码43的根源,并提供了从环境检查、核心PowerShell命令(如Set-VMGpuPartitionAdapter)配置到虚拟机内驱动替换的完整实战解决方案,帮助用户彻底解决显卡驱动无法加载的问题。

weixin_29238147的博客 893

浅谈python反序列化漏洞

0x01 python序列化与反序列化 Python 的序列化和反序列化是将一个类对象向字节流转化从而进行存储和传输,然后使用的时候再将字节流转化回原始的对象的一个过程。python中的序列化操作是通过pickle模块,而该模块主要包含l dumps将dict转化为str格式,loads将str格式转化为dict格式。 dump和load与文件操作结合起来, pickle.dump(obj, f...

Lethe's Blog 3219

万字整理JavaSE核心知识:从面向对象到并发编程的实战指南

面向对象编程是Java语言的基石,其核心思想包括封装、继承和多态,旨在通过抽象和模块化提升代码的可维护性与复用性。在Java标准版中,集合框架、IO流和多线程并发是支撑工程实践的关键技术组件。集合框架如HashMap通过数组、链表和红黑树的数据结构实现高效的数据存储与检索,其扩容机制和线程安全问题是开发中的常见考点。多线程并发编程则涉及线程生命周期管理、同步机制以及JMM内存模型,其中synchronized关键字和volatile变量分别用于保证原子性与可见性,是构建高并发应用的基础。这些技术共同服务于企

aodiyi6351的博客 394

网格质量评价

CFD中评价网格质量的方法

B_etter_Man的博客 1万+

JDK源码 AbstractQueueSynchronizer 笔记

AbstractQueueSynchronizer笔记在开始介绍之前我先来简单理解一下为什么会产生AQS。在我们应用内不可避免的会发生对一些资源的抢占,那么如何处理线程之间对资源的争夺呢?在Java SE 5 之前JDK可以使用synchronized来串行化对资源的操作,synchronized可以隐式的获取和释放锁,但是带来的不便就是不够灵活,可扩展性没有显式获取和释放锁的自主控制性强,另外sy

少爷 466

C/C++ 静态代码检查工具cppCheck

Cppcheck是一个用于C/C++代码的静态分析工具,它可以帮助开发者检测代码中的错误;Cppcheck可以检测出许多类型的错误,包括语法错误、未使用的函数、内存泄漏、未初始化的变量等;Cppcheck还支持用户自定义规则,这使得开发者可以根据自己的需求定制Cppcheck的行为;使用--suppress如果你想要忽略某些警告,可以在命令行中使用 --suppress 选项。

贝勒里恩的博客 4946

9篇2章2节:GBD 数据库的数据申请详解

无论您是初入公共卫生领域的学生、资深的科研工作者,还是政策决策者,GBD数据库都将成为您数据分析与决策支持中不可或缺的重要工具。

MD Code 2596

Java线程安全杂谈——锁、状态依赖与协同以及锁优化

从谈Java并发开始synchronized和锁就时常被谈到,上篇讲Java内存模型特点的时候,也说道用synchronized几乎可以同时满足原子性、可见性和有序性三点,那本篇就来说一下锁的概念、synchronized和API层面Lock锁框架的比较选择。后面也会讲到状态依赖与协同问题、条件队列和锁优化。 先说说synchronized。synchronized关键字可谓是并发里的常见词,...

milagroo的博客 129

星载SAR的各项指标解读(史上最全)

一、系统指标 系统指标主要指雷达系统的通用的指标,以及和卫星整星相关的指标,主要有一下几项: 工作频段: 雷达体制: 极化方式: 设计s

9268

Java 锁的那些事儿

Java 多线程开发中,如果涉及到共享资源操作场景,那就必不可少要和 Java 锁打交道。 Java 中的锁机制主要分为Lock和Synchronized,本文主要分析 Java 锁机制的使用和实现原理,按照 Java 锁使用、JDK 中锁实现、系统层锁实现的顺序来进行分析,话不多说,let’s go~ 一、Java 锁使用 在 Lock 接口出现之前,Java 程序是靠 synchronized 关键字实现锁功能的,而 JavaSE 5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来..

740

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

WINCC7.3破解文件

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

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 214

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

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

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 317

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

【JVM】对象内存布局

HotSpot JVM 中,Java 对象内存布局由对象头(12字节,含Mark Word与压缩Klass Pointer)、实例数据及对齐填充构成,总大小为8字节倍数。对象头支持锁状态动态复用,指针压缩使引用仅4字节,但堆超32GB时失效。实例字段按类型优先级重排序以减少间隙,最小对象大小为16字节。通过JOL工具可精准验证布局,理解锁机制、GC、伪共享等底层行为。

2301_79732484的博客 137

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 108

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 186

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 346

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 100

数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南

Oracle 提供了ORA_ROWSCN伪列(System Change Number),记录最后修改行的 SCN,类似 PostgreSQL 的xmin。实践中通常仍使用 version 字段方案,逻辑与 MySQL 完全相同。-- Oracle:通过 ORA_ROWSCN 实现乐观锁-- 更新时比对 SCN// 自定义注解// AOP 切面@Aspect@Component@Slf4jtry {

yjc_1111的专栏 389

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 603

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 367

java框架大全

java框架大全

qq_39326137的博客 179

SecureRandom做随机源,这个Java随机字符串工具类有3个方法

本文分享一款JDK17环境下的Java随机字符串工具类,内置3个开箱即用的方法,全部采用SecureRandom加密安全随机源,不存在弱随机、可预测、取模偏差等问题。工具类支持默认62字符集随机串、纯数字随机串、自定义字符集随机串三种生成模式,支持Unicode码点级别的字符集处理,兼容Emoji等增补字符,边界处理严格,异常信息明确,适合验证码、邀请码、盐值、随机口令、短链码等场景,源码可直接复制使用。

Java后端 | AI智能体应用实战派 | 电脑技能行家 290
上一篇: 8.2-spring 事务-声明式事务(@Transactional原理)
下一篇: JavaSE-多线程(5.3)- ReentrantLock (源码解析,非公平模式)
sql2008help
博客等级 码龄11年 80粉丝 145原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值