Análisis de código fuente AQS multiproceso de Java

Análisis de código fuente AQS

AQS (AbstractQueueedSynchronizer) utiliza una variable miembro int para representar el estado de sincronización. El trabajo en cola para completar la adquisición de recursos a través de la cola FIFO incorporada
La imagen proviene del caballo soldado educación multihilo
es garantizar la visibilidad del subproceso de variable de estado.
AQS cambia el estado principalmente de las siguientes tres formas

getState()
setState()
compareAndSetState()

La ventaja de usar CAS para agregar nodos a la cola es agregar nodos
sin bloquear toda la lista vinculada

método lock () exclusivo

Método de adquisición de la primera llamada sincronizada

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

Ingrese AQS y llame al método acqurie ()

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

La primera rama ejecuta tryAcquire () ,

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

Después de hacer clic en el interior, encontré no justo TryAcquire ()

/**
*尝试获取锁,成功返回true,失败返回false
**/
final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取当前的标志的状态
            int c = getState();
            //c==0表示没人获取这把锁
            if (c == 0) {
            	//使用CAS操作尝试获取这把锁
                if (compareAndSetState(0, acquires)) {
                	//如果获取成功,设置为独占线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果该线程已经占有了该锁,将标志位加1,这就是可重入的原理
            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;
        }

Si tryAccquire () devuelve falso, ingrese la rama adquirirQueued (addWaiter (Node.EXCLUSIVE), arg).
Primero, mire el código fuente de addWaiter ()

private Node addWaiter(Node mode) {
		//创建一个新的节点
        Node node = new Node(mode);
		//for是个死循环,不达目的不罢休
        for (;;) {
        	//tail是等待队列的尾巴节点
            Node oldTail = tail;
            if (oldTail != null) {
            	//尾部节点不为空,用setPrevRelaxed()函数设置
            	//node的上一节点
                node.setPrevRelaxed(oldTail);
                //通过CAS操作加入等待队列
                if (compareAndSetTail(oldTail, node)) {
                    oldTail.next = node;
                    return node;
                }
            } else {
                initializeSyncQueue();
            }
        }
    }
    //初始化等待队列
private final void initializeSyncQueue() {
        Node h;
        if (HEAD.compareAndSet(this, null, (h = new Node())))
            tail = h;
    }

Ver el método adquirirQueued a continuación

final boolean acquireQueued(final Node node, int arg) {
        boolean interrupted = false;
        try {
        	//死循环
            for (;;) {
            	//p为node的前驱
                final Node p = node.predecessor();
                //如果p为头节点,tryAcquire成功
                if (p == head && tryAcquire(arg)) {
                	//设置node为头节点,也就是获取锁
                    setHead(node);
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node))
                    interrupted |= parkAndCheckInterrupt();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            if (interrupted)
                selfInterrupt();
            throw t;
        }
    }

    /**
     * 如果尝试获取锁失败,检查并更新节点状态.
     * 如果节点线程被block住就返回true
     *
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             * 
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             * 大于0跳过节点
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
        }
        return false;
    }


waitStatus tiene principalmente el siguiente estado

        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. 后继unpark*/
        static final int SIGNAL    = -1;
        /** waitStatus value to indicate thread is waiting on condition. condition队列*/
        static final int CONDITION = -2;
        /**
         * waitStatus value to indicate the next acquireShared should
         * unconditionally propagate.
         */
        static final int PROPAGATE = -3;

Señal
El sucesor de este nodo está (o pronto lo estará) bloqueado (a través del estacionamiento), por lo que el nodo actual debe desconectar a su sucesor cuando libera orcanceles. Para evitar las razas, los métodos de adquisición primero deben indicar que necesitan una señal, luego vuelva a intentar la adquisición atómica. , y luego, en caso de fallo, bloquear. El
nodo actual debe ser un nodo sucesor unpark () cuando el nodo actual libera recursos o lo cancela

Desbloquear () exclusivo

Ingresar lanzamiento

    public void unlock() {
        sync.release(1);
    }
    public final boolean release(int arg) {
    	//释放资源成功
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            //唤醒后继
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease ()

//当c=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;
        }

Bloqueo de lectura y escritura

main(){
ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
        reentrantLock.readLock().lock();
}

Llame a sync.acqurireShared ()

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

Método de plantilla de llamada

   public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

El valor de retorno del método tryAcquireShared (int arg) es de tipo int. Cuando el valor de retorno es mayor o igual a 0, indica que se puede obtener el estado de sincronización
doAcquireShared () unirse a la cola de espera

Inserte la descripción de la imagen aquí
El bit indicador es de 32 bits. El
estado de lectura es alto de 16 bits y el estado de escritura es bajo de 16 bits.
Estado de escritura = estado de state&0x0000FFFF
lectura = state>>>16
estado de escritura más 1 = estado + 1
estado de lectura más 1 = state+(1<<<16)
cuando estado> 0 state&0x0000FFFF=0,state>>>16 > 0, bloqueo de lectura adquiere
estado> 0 state&0x0000FFFF>0,state>>>16 >=0, bloqueo de escritura Obtener`

Bloquear degradación

La degradación de bloqueo se refiere a la degradación de un bloqueo de escritura en un bloqueo de lectura. Si el hilo actual posee el bloqueo de escritura, luego lo libera y finalmente adquiere el bloqueo de lectura, este proceso de finalización de la segmentación no puede llamarse degradación del bloqueo. La degradación de bloqueo se refiere al proceso de mantener el bloqueo de escritura (actualmente propiedad), adquirir el bloqueo de lectura y luego liberar el bloqueo de escritura (propiedad anterior)

8 artículos originales publicados · Me gusta1 · Visitas 169

Supongo que te gusta

Origin blog.csdn.net/qq_41725735/article/details/105268037
Recomendado
Clasificación