首页 > 编程知识 正文

cas锁底层原理,可重入锁原理cas

时间:2023-05-04 16:31:20 阅读:129243 作者:3615

转载: http://www.blogjava.net/xylz/archive/2010/07/04/325206.html

33558 www.tui cool.com/articles/zuui 6z

JDK1.5之前的Java使用同步关键字保证同步。 因此,通过使用一致的锁定协议来协调对共享状态的访问,无论哪个线程具有守护程序变量的锁定,都可以独占地访问这些变量。

一.锁定1.1 .锁定机制存在的问题(1)在多线程竞争下,锁定或解除会导致上下文切换和调度延迟较多,引起性能问题。 此外,在上下文切换期间,在cpu之前缓存的指令和数据将被禁用,从而导致性能严重损失。 用户状态锁定避免了这些问题,但只有在实际上没有实际竞争时才有效。

)2)如果一个线程拥有锁定,则需要此锁定的所有其他线程将挂起,直到解除锁定。

)3)如果高优先级线程等待低优先级线程解除锁定,则优先级反转,从而导致性能风险。

1.2 .悲观锁定和乐观锁定是悲观锁定,同步是独占锁定,等待所有其他需要锁定的线程挂起,且具有锁定的线程解除锁定。 另一个更有效的关键是乐观的关键。 乐观锁定是指每次都不锁定,完成假设没有冲突的操作,如果冲突失败,则重试直到成功。

2 .与锁定相比,2.volatile变量是一个或多个轻量级同步机制。 使用这些变量时,不会发生上下文切换和线程调度等操作,但volatile变量有一些限制。 由于不能用于构建原子的复合操作,因此如果一个变量依赖旧值,则不能使用volatile变量。

Java中的原子操作(atomic operations )是指原子操作在一步内完成,不中断。 原子操作在多线程环境中是线程安全的,不需要考虑同步问题。 long型赋值不是原子操作吗? 实时访问java时,此长变量分为两个阶段写入。 首先写32位,然后写32位。 这样线程就不安全了。 如果更改为以下内容,则是线程安全:

私密电压长输;

因为在volatile内部已经建立了同步。

三、实现CAS无锁算法无锁(lock-free )的无阻塞算法有几种实现方法,其中CAS )比较和swap CAS的词义是“我认为v的值应该是a。 如果是,则将v的值更新为b。 否则,就不修正v的值实际是多少来告诉你”。 CAS是一种乐观锁定技术,当多个线程尝试使用CAS同时更新同一变量时,只有一个线程可以更新变量值,但所有其他线程都失败,失败的线程不会被挂起,在这次竞争中被告知失败,并且此外,仅当期望值a和存储器值v相同时,将存储器值v改变为b,否则不执行任何操作。

CAS是一种基于原子操作采用特定编程方法(事务-提交-提交-失败-重试)的机制,访问共享资源的线程不依赖于其他线程的调度和执行,只需少量的步骤即可完成

一个线程的失败或挂起不应影响另一个线程的失败或挂起的算法。 现代CPU提供了自动更新共享数据和检测其他线程干扰的特殊指令,但compareAndSet ) )使用它们而不是锁定。

取出AtomicInteger,探讨在没有锁定的情况下如何实现数据的正确性。

私有电压输入值;

首先,没有锁定的机制无疑需要利用volatile原语来确保线程之间的数据是可见的(共享的)。 这样第一次获取变量值时可以直接读取。

公共金融入门

返回值;

}

然后看看I是怎么做的。

公共final int增量角度get (

for (; () )。

int current=get (;

int next=current 1;

if (比较集(当前,下一步) )

返回下一步;

}

}

这里采用的是CAS操作,每次从内存中读取数据,对该数据和1后的结果进行CAS操作,成功则返回结果,否则重试直到成功。 compareAndSet利用JNI完成CPU指令的操作。

公共金融

l boolean compareAndSet(int expect, int update) { 

       return unsafe.compareAndSwapInt(this, valueOffset, expect, update);

   }

   整体的过程就是这样子的,利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。其它原子操作都是利用类似的特性完成的。而整个J.U.C都是建立在CAS之上的,因此对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。

   CAS看起来很爽,但是会导致“ABA问题”。CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。这允许一对变化的元素进行原子操作。

四.JVM对CAS的支持

   AtomicInt, AtomicLong.incrementAndGet()

   在JDK1.5之前,如果不编写明确的代码就无法执行CAS操作,在JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令,如果处理器/CPU不支持CAS指令,那么JVM将使用自旋锁。

   在原子类变量中,如java.util.concurrent.atomic中的AtomicXXX,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作,而在java.util.concurrent中的大多数类在实现时都直接或间接的使用了这些原子变量类。


五.高并发环境下优化锁或无锁(lock-free)的设计思路

   高并发环境下要实现高吞吐量和线程安全,两个思路:

   一个是用优化的锁实现,

   一个是lock-free的无锁结构。

   但非阻塞算法要比基于锁的算法复杂得多。开发非阻塞算法是相当专业的训练,而且要证明算法的正确也极为困难,不仅和具体的目标机器平台和编译器相关,而且需要复杂的技巧和严格的测试。虽然Lock-Free编程非常困难,但是它通常可以带来比基于锁编程更高的吞吐量。所以Lock-Free编程是大有前途的技术。它在线程中止、优先级反转以及信号安全等方面都有着良好的表现。

 优化锁实现的例子 :Java中的ConcurrentHashMap,设计巧妙,用桶粒度的锁和锁分离机制,避免了put和get中对整个map的锁定,尤其在get中,只对一个HashEntry做锁定操作,性能提升是显而易见的。Lock-free无锁的例子 :CAS(Compare-And-Swap)的利用和LMAX的disruptor 无锁消息队列数据结构等。例如ConcurrentLinkedQueue。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。