为了账号安全,请及时绑定邮箱和手机立即绑定

Java中的锁

标签:
Java

AbstractQueuedSynchronizer

  • 该类是一个实现同步器的框架, 包含了一个FIFO队列, 一个state
  • state值:0代表unlocked state,1代表locked state

使用方法

  • 继承AbstractQueuedSynchronizer
  • 实现tryAcquire(int arg)
  • 实现tryRelease(int arg)
  • 实现tryAcquireShared(int arg)
  • 实现tryReleaseShared(int arg)

原理

  • 获取锁–独享模式
	public final void acquire(int arg) {
	    if (!tryAcquire(arg) &&
	        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
	        selfInterrupt();
	}

最终会调用我们实现的trytryAcquire(). 如果返回true说明获取锁成功, 如果返回false说明获取锁失败, 那就把当前线程添加到FIFO队列中, 并调用park将线程挂起。

  • 释放锁–独享模式
	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, 返回true说明释放锁成功, 那么就将FIFO队列中的挂起的线程通过unpark恢复。

  • 获取锁–共享模式
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
    
    private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
	                    //这里实现唤醒共享模式的节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

共享模式在获取到锁之后会调用setHeadAndPropagate, doReleaseShared唤醒队列中是共享模式的节点。

  • 释放锁–共享模式
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

可重入锁ReentrantLock

  • 可重入锁意思是, 拥有该锁的线程可以多次拥有该锁, 不需要等待.
  • 公平锁:线程A想要获取锁,如果FIFO队列中有等待的线程,那么线程A一定会进入到FIFO排队等待.
  • 不公平锁:线程A想要获取锁,不管FIFO队列中是否有等待的线程,线程A会与他们一起竞争锁资源.
  • 原理如下
	protected final boolean tryAcquire(int acquires) {
	   final Thread current = Thread.currentThread();
	   int c = getState();
	   if (c == 0) {
	    //公平锁的实现,如果当前FIFO队列中有等待的线程,那么就加入到等待队列中
	       if (!hasQueuedPredecessors() &&
	           compareAndSetState(0, acquires)) {
	           setExclusiveOwnerThread(current);
	           return true;
	       }
	   }
	   //可重入锁的实现,如果当前线程已经拥有锁了,那么计数器+1.
	   else if (current == getExclusiveOwnerThread()) {
	       int nextc = c + acquires;
	       if (nextc < 0)
	           throw new Error("Maximum lock count exceeded");
	       setState(nextc);
	       return true;
	   }
	   return false;
	}
	final boolean nonfairTryAcquire(int acquires) {
	    final Thread current = Thread.currentThread();
	    int c = getState();
	    if (c == 0) {
		    //不公平锁的实现,只要调用了获取锁的方法就会去竞争资源.
	        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;
	}
	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;
	}

CountDownLatch

  • 基于共享模式实现的.
	int tryAcquireShared(int acquires) {
	    return (getState() == 0) ? 1 : -1;
	}
	protected boolean tryReleaseShared(int releases) {
	    // Decrement count; signal when transition to zero
	    for (;;) {
	        int c = getState();
	        if (c == 0)
	            return false;
	        int nextc = c-1;
	        if (compareAndSetState(c, nextc))
	            return nextc == 0;
	    }
	}
	public void await() throws InterruptedException {
	    sync.acquireSharedInterruptibly(1);
	}
    public void countDown() {
        sync.releaseShared(1);
    }
  • 当调用await时
  1. 调用tryAcquireShared
  2. 如果返回值<0, 把线程加入到FIFO队列中
  3. 挂起线程
  • 当调用countDown时
  1. 调用tryReleaseShared
  2. 如果state != 0 就返回失败
  3. 如果state == 0那么就唤醒队列中共享模式的线程. (独占模式中只唤醒FIFO队列中的一个线程)

Condition

  • 锁中Condition提供的await, signal相当于 synchronized中的wait, notify.
await流程:
1. 创建节点加入到条件队列
2. 释放互斥锁
3. 只要没有转移到同步队列就阻塞(等待其他线程调用signal/signalAll或是被中断)
4. 重新获取互斥锁
signal流程:
1. 将队列中第一个节点转移到同步队列
2. 根据情况决定是否要唤醒对应线程
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
1
获赞与收藏
31

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消