Java CAS 和 synchronized 和 Lock

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_17612199/article/details/80385737

CAS 机制

  1. 适用场景:乐观认为并发不高,不需要阻塞,可以不上锁。
  2. 特点:不断比较更新,直到成功。
  3. 缺点:高并发cpu压力大;ABA问题。

ABA问题:
CAS机制生效的前提是,取出内存中某时刻的数据,而在下时刻比较并替换。
如果在比较之前,数据发生了变化,例如:A->B->A,即A变为B然后又变化A,那么这个数据还是发生了变化,但是CAS还是会成功。

Java中CAS机制使用版本号进行对比,避免ABA问题。

synchronized

  1. 适用场景:悲观认为并发很高,需要阻塞,需要上锁。
  2. 特点:语言层面的优化,锁粗化、偏向锁、轻量锁等等;可读性高。

ReentrantLock 和 Atomic类

以上两种并发工具都使用了CAS机制。
在并发不高竞争不激烈时候,性能略低于synchronized;相反,并发高竞争激烈时候,性能高于synchronized。

ReentrantLock相对于synchronized:

  • ReentrantLock等待可中断,synchronized不可以。
  • ReentrantLock需要手动释放锁,synchronized不需要。
  • ReentrantLock可支持公平非公平锁,synchronized只支持非公平锁。
  • ReentrantLock没有语言层面的优化,底层实现机制AQS和CAS,synchronized有优化。
  • ReentrantLock可重入锁,synchronized不可重入,可能导致死锁。
  • ReentrantLock支持读写锁,可以提高高并发读操作。
  • synchronized由操作系统支持,涉及内核态和用户态的上下文切换,并发高时切换开销非常大。
  • ReentrantLock(AQS)依赖volatile int变量标示锁状态,结构为双向链表的等待队列,通过(cas+死循环)更改锁状态,一旦更新成功,标示竞争到锁。

AQS释放锁:
通过cas改变状态

    private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

获取锁:
如果获取到锁,设为头节点,否则一直自旋等待,在高并发时,可以避免大量锁竞争的上下文切换,降低线程切换开销。

private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        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);
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_17612199/article/details/80385737