《Java高并发程序设计》 --通过ReentrantLock观望Java锁机制

多线程锁有两种: 

 一种是用关键字 : syncronized实现

另一种是用Lock的实现类实现。

 

关于syncronized的锁,以及锁升级的解释可以参考一位博主写的(https://www.cnblogs.com/yuhangwang/p/11295940.html

 

这里就先看ReentrantLock的实现,去窥探java的锁机制。

首先整体流程图如下:

 

 

 

示例代码:

public class TestTryLock implements Runnable {
    private static Lock locks = new ReentrantLock();
    @Override
    public void run() {
        try {
            if(locks.tryLock(4, TimeUnit.SECONDS)){  // lock.lock()
                System.out.println(Thread.currentThread().getName()+"-->");
                Thread.sleep(3000);
            }else{
                System.out.println(Thread.currentThread().getName()+" time out ");
            }
        } catch (InterruptedException e) {
            // e.printStackTrace();
        }finally {
            locks.unlock();//会抛出锁对象的异常,因为没有获取锁在unlock的时候出异常,可以先判断一下是否存在在执行。
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestTryLock test =new TestTryLock();
        Thread t1 = new Thread(test,"t1");
        Thread t2 = new Thread(test,"t2");
        Thread t3 = new Thread(test,"t3");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

 

示例用使用的是 ReentrantLock的非公平锁的实现,即线程不需要按照申请资源的顺序进行抢占锁资源。

通过使用lock.lock()  和lock.unlock()的配合,进行对资源的加解锁。

final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

 

lock.lock() 是线程对资源进行绑定的操作,当线程占有了该共享资源(lock)的时候,会在 AQS同步器中的 

exclusiveOwnerThread 属性设置当前抢占线程的值。 并将state设置为 1 。

如果没有抢占到资源的时候,就会走acquire(1)的路子。 也就是会将没有抢到锁的线程丢进等待队列中,这是一个FIFO队列

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
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;
}

然后不断的进行自旋等待,判断前一节点是不是头节点,当原先抢占了资源锁的线程进行lock.unlock()等操作时,

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

也就是调用了AQS的release操作。 就会将state - 1 处理,一旦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;
        }

 

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

 

如果自旋等待的队列中某节点的前一节点是头节点,该节点就开始申请抢占 AQS中的

exclusiveOwnerThread。 如果成功了,就离开等待队列,进入到运行状态。


以上。

posted @ 2021-01-08 18:34  一只喜鹊  阅读(69)  评论(0编辑  收藏  举报