Interrupción del análisis de código fuente AQS y adquisición de bloqueo de tiempo de espera

Interrupción del análisis de código fuente AQS y adquisición de bloqueo de tiempo de espera

Base de interrupción

  • Thread.interrupt (): método de objeto, establece el indicador de interrupción en verdadero.
  • Thread.currentThread (). IsInterrupted (): El método de objeto devuelve el estado actual del indicador de interrupción del hilo sin borrar el bit del indicador de interrupción.
  • Thread.interrupted (): un método estático que devuelve el estado actual del indicador de interrupción del hilo y borra el bit del indicador de interrupción (establecido en falso).
package com.morris.concurrent.lock.reentrantlock.trace;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class InterruptDemo {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    

        Thread t1 = new Thread(() -> {
    
    
            LockSupport.park();
            System.out.println("t1:" + Thread.interrupted()); // true
            System.out.println("t1:" + Thread.currentThread().isInterrupted()); // false
        });
        t1.start();

        TimeUnit.SECONDS.sleep(1);
        t1.interrupt();
        System.out.println("main:" + t1.isInterrupted()); // true
    }
}

Análisis del código fuente del método de interrupción lockInterruptiblemente ()

package com.morris.concurrent.lock.reentrantlock.trace;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示独占锁的中断
 */
public class InterruptThreadDemo {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        ReentrantLock reentrantLock = new ReentrantLock();
        new Thread(() -> {
    
    
            reentrantLock.lock();
            try {
    
    
                System.out.println("t1");
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                reentrantLock.unlock();
            }
        }, "t1").start();

        TimeUnit.SECONDS.sleep(1); // 等待t1启动

        Thread t2 = new Thread(() -> {
    
    
            try {
    
    
                reentrantLock.lockInterruptibly();
                try {
    
    
                    System.out.println("t2 get lock");
                } finally {
    
    
                    reentrantLock.unlock();
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, "t2");
        t2.start();

        TimeUnit.SECONDS.sleep(1); // 等待t2启动

        t2.interrupt(); // 中断t2,会抛出InterruptedException异常
    }

}

java.util.concurrent.locks.ReentrantLock # lockInterruptiblemente

public void lockInterruptibly() throws InterruptedException {
    
    
    sync.acquireInterruptibly(1);
}

java.util.concurrent.locks.AbstractQueuedSynchronizer # adquirirInterruptamente

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    
    
    if (Thread.interrupted()) // 还没获取锁之前就中断了就直接抛出异常
        throw new InterruptedException();
    if (!tryAcquire(arg)) // 尝试获取锁,与不可中断的逻辑一致,区分公平与非公平
        doAcquireInterruptibly(arg);
}

java.util.concurrent.locks.AbstractQueuedSynchronizer # doAcquireInterruptbly

private void doAcquireInterruptibly(int arg)
    throws InterruptedException {
    
    
    final Node node = addWaiter(Node.EXCLUSIVE); // 将当前线程封装成Node节点加入到同步队列尾部
    boolean failed = true;
    try {
    
    
        for (;;) {
    
    
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
    
    
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException(); // 被中断了直接抛出异常
        }
    } finally {
    
    
        if (failed)
            cancelAcquire(node);
    }
}

El bloqueo de método ininterrumpido () maneja la interrupción

package com.morris.concurrent.lock.reentrantlock.trace;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示独占锁中不可中断方法lock()对中断的处理
 */
public class InterruptThreadDemo2 {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    

        ReentrantLock reentrantLock = new ReentrantLock();
        new Thread(() -> {
    
    
            reentrantLock.lock();
            try {
    
    
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                reentrantLock.unlock();
            }
        }, "t1").start();

        TimeUnit.SECONDS.sleep(1); // 等待t1启动

        Thread t2 = new Thread(() -> {
    
    
            reentrantLock.lock();
            try {
    
    
                System.out.println("t2: " + Thread.currentThread().isInterrupted()); // true
            }finally {
    
    
                reentrantLock.unlock();
            }
        }, "t2");
        t2.start();

        TimeUnit.SECONDS.sleep(1); // 等待t2启动

        t2.interrupt(); // 中断t2
        System.out.println("main: " + t2.isInterrupted()); // false
    }

}

A partir de los resultados de la ejecución, se puede encontrar que cuando se interrumpe t2, el bit de bandera de interrupción de t2 debe ser verdadero, pero debido a que el bloqueo se está adquiriendo en este momento (ininterrumpible), AQS establece este bit de bandera de interrupción en falso primero, esperando la adquisición. Una vez alcanzado el bloqueo, restablezca el bit del indicador de interrupción a verdadero nuevamente.

El siguiente análisis del código fuente:

Ubique directamente java.util.concurrent.locks.AbstractQueuedSynchronizer # adquiridoQueued

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

Después de ejecutar el método anterior, vuelva al método siguiente.

java.util.concurrent.locks.AbstractQueuedSynchronizer # adquirir

public final void acquire(int arg) {
    
    
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt(); // 线程被中断了会进入到这里
}

static void selfInterrupt() {
    
    
    Thread.currentThread().interrupt(); // 自己把自己中断,置中断标记位为true
}

Análisis de código fuente de adquisición de bloqueo de tiempo de espera

El uso de la adquisición de bloqueo de tiempo de espera es el siguiente:

package com.morris.concurrent.lock.reentrantlock.trace;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 演示独占锁的超时
 */
public class TimeoutThreadDemo {
    
    

    public static void main(String[] args) throws InterruptedException {
    
    
        CountDownLatch countDownLatch = new CountDownLatch(1);

        ReentrantLock reentrantLock = new ReentrantLock();
        new Thread(()->{
    
    
            reentrantLock.lock();
            try {
    
    
                countDownLatch.countDown();
                System.out.println("t1");
                TimeUnit.SECONDS.sleep(30);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            } finally {
    
    
                reentrantLock.unlock();
            }
        }, "t1").start();

        countDownLatch.await();

        new Thread(()->{
    
    
            try {
    
    
                if(reentrantLock.tryLock(3, TimeUnit.SECONDS)) {
    
    
                    System.out.println("t2 get lock");
                    reentrantLock.unlock();
                } else {
    
    
                    System.out.println("t2 get lock timeout");
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }, "t2").start();
    }

}

java.util.concurrent.locks.ReentrantLock # tryLock (largo, java.util.concurrent.TimeUnit)

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException {
    
    
    return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

java.util.concurrent.locks.AbstractQueuedSynchronizer # tryAcquireNanos

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    
    
    if (Thread.interrupted()) // 还没获取锁之前就中断了就直接抛出异常
        throw new InterruptedException();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

java.util.concurrent.locks.AbstractQueuedSynchronizer # doAcquireNanos

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    
    
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout; // 超时时间点
    final Node node = addWaiter(Node.EXCLUSIVE); // 将当前线程封装成Node节点加入到同步队列尾部
    boolean failed = true;
    try {
    
    
        for (;;) {
    
    
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
    
    
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return true;
            }
            nanosTimeout = deadline - System.nanoTime(); // 剩余等待时间
            if (nanosTimeout <= 0L)
                return false;
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold) // 超时时间小于1000纳秒则直接自旋,不会休眠
                LockSupport.parkNanos(this, nanosTimeout); // 带超时时间的休眠
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
    
    
        if (failed)
            cancelAcquire(node);
    }
}

Supongo que te gusta

Origin blog.csdn.net/u022812849/article/details/108748535
Recomendado
Clasificación