- 问题背景
最近在看LinkedBlockingQueue看到了其中的count使用AtomicInteger修饰,之前也看过AtomicInteger的一些解释,也是似懂非懂的,今天深入的了解了其实现方式,学到了很多东西。
- 基础介绍
要对AtomicInteger有一个深入的认识,就必须要了解一下悲观锁和乐观锁。cpu是时分复用的,也就是把cpu的时间片,分配给不同的线程/进程轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
但是,由于在进程挂起和恢复执行过程中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。举个例子,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可能立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。
所以就有了乐观锁的概念,他的核心思路就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。在上面的例子中,某个线程可以不让出cpu,而是一直while循环,如果失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。比如我们要说的AtomicInteger底层同步CAS就是一种乐观锁思想的应用。
CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
在CAS操作中,会出现ABA问题。就是如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。
- AtomicInteger分析
Atomic包下类的理解:
Atomic包是Java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。可以对基本数据、数组中的基本数据、对类中的基本数据进行操作。原子变量类相当于一种泛化的volatile变量,能够支持原子的和有条件的读-改-写操作。—— 引自@chenzehe 的博客。
先来看一下AtomicInteger中getAndIncrement()方法的实现:
1 public final int getAndIncrement() { 2 for (;;) { 3 int current = get(); 4 int next = current + 1; 5 if (compareAndSet(current, next)) 6 return current; 7 } 8 }
这个方法的做法为先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码如下:
1 public final boolean compareAndSet(int expect, int update) { 2 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 3 }
compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所做的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好。
下面以具体的例子分析下AtomicInteger的实现:
计数器(Counter),采用Java里比较方便的锁机制synchronized关键字,初步的代码如下:
1 public class Counter { 2 private int value; 3 4 public synchronized int getValue() { 5 return value; 6 } 7 8 public synchronized int increment() { 9 return ++value; 10 } 11 12 public synchronized int decrement() { 13 return --value; 14 } 15 }
synchronized关键字是基于阻塞的锁机制,也就是说当一个线程拥有锁的时候,访问同一资源的其它线程需要等待,直到该线程释放锁,这也就我们前面所说的悲观锁。这里会有些问题:首先,如果被阻塞的线程优先级很高很重要怎么办?其次,如果获得锁的线程一直不释放锁怎么办?(这种情况是非常糟糕的)。还有一种情况,如果有大量的线程来竞争资源,那CPU将会花费大量的时间和资源来处理这些竞争(事实上CPU的主要工作并非这些),同时,还有可能出现一些例如死锁之类的情况,最后,其实锁机制是一种比较粗糙,粒度比较大的机制,相对于像计数器这样的需求有点儿过于笨重,因此,对于这种需求我们期待一种更合适、更高效的线程安全机制。
下面我们就以模拟CAS机制来实现Counter的例子:
CAS类:
1 public class SimpleCAS { 2 private volatile int value; 3 public synchronized int getValue(){ 4 return value; 5 } 6 public synchronized boolean comperaAndSwap(int expectedValue,int newValue){ 7 int oldValue = value; 8 if(oldValue == expectedValue){ 9 value = newValue; 10 return true; 11 }else{ 12 return false; 13 } 14 } 15 }
CASCounter类:
1 public class CASCounter { 2 private SimpleCAS cas; 3 public int getValue(){ 4 return cas.getValue(); 5 } 6 public int increment(){ 7 int olevalue = cas.getValue(); 8 for (; ;) { 9 if(cas.comperaAndSwap(olevalue, olevalue+1)){ 10 return cas.getValue(); 11 } 12 } 13 14 } 15 }
上面的模拟不是CSA的真正实现,其实我们在语言层面是没有做任何同步的操作的,大家也可以看到源码没有任何锁加在上面,可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!
总结一下,AtomicInteger基于冲突检测的乐观并发策略。 可以想象,这种乐观在线程数目非常多的情况下,失败的概率会指数型增加。
相关推荐
本项目通过结合ViewPager和AtomicInteger实现了一个高效的广告轮播功能,具有自动轮播和响应用户触摸事件的能力。 首先,`ViewPager`是Android SDK提供的一种强大的组件,主要用于展示可滑动的页面集合。在这个案例...
AtomicInteger 是Java并发编程中实现原子操作的重要工具。它通过 Unsafe 类提供的硬件级别的原子操作和 volatile 关键字保证了操作的原子性和可见性。在实际开发中,我们应该根据具体的应用场景选择合适的同步机制。...
AtomicInteger是Java并发包下面提供的原子类,主要操作的是int类型的整型,通过调用底层Unsafe的CAS等方法实现原子操作。下面是对AtomicInteger的源码分析。 1. 什么是原子操作? 原子操作是指不会被线程调度机制...
这通常通过CAS(Compare and Swap)操作来实现,这是一种硬件支持的低级别原子操作。 相比之下,普通的`int`变量在多线程环境下进行修改时,如果不采取额外的同步措施(如`synchronized`关键字或`Lock`对象),可能...
2. **性能优势**:相比于使用锁机制(如`synchronized`),`AtomicInteger`通过CAS算法实现了更高的并发性能。这是因为锁机制在高并发场景下可能导致线程阻塞,而CAS则可以避免这种情况。 3. **简化代码**:使用`...
在Java并发编程中,无锁实现是一个高级技术,它可以让多个线程在没有使用传统锁机制(如synchronized关键字或显示锁Lock)的情况下,安全地执行对共享资源的操作。无锁机制主要依赖于硬件的原子指令,尤其是比较并...
监视器机制不仅实现了互斥访问,还允许线程之间通过`wait`和`notify`操作进行合作。 - **互斥访问**:监视器确保同一时刻只有一个线程可以执行某个对象的临界区代码。 - **等待与通知**:线程可以通过调用监视器中...
Java 使用 ConcurrentHashMap 和计数器实现锁是 Java 编程语言中的一种常见的锁机制实现方式。该机制主要通过使用 ConcurrentHashMap 和计数器来实现线程之间的同步和排队。 ConcurrentHashMap 介绍 ...
- **接口实现**:类可以实现多个接口,实现多继承的效果。 10. **异常处理**: - **try-catch-finally**:异常的捕获与处理。 - **throw与throws**:明确抛出异常和声明可能抛出的异常。 11. **集合框架**: -...
该包中的大多数类都使用 CAS 操作来实现同步机制,例如 AtomicInteger、AtomicLong 等原子变量类。 CAS 操作的原理可以通过分析 CPU 底层指令来理解。以 Intel x86 CPU 为例,CAS 操作可以通过 LOCK CMPXCHG 指令来...
这种机制是乐观锁的一种实现,因为它假设大多数情况下不会有冲突,即使有冲突也能快速解决。 CAS操作包含三个参数:内存位置V、预期值A和新值B。如果内存位置V的值等于预期值A,则将V的值设置为B,否则不做任何操作...
在深入探讨Java虚拟机(JVM)如何处理线程间的资源同步与交互机制之前,我们先来明确几个关键概念:线程、多线程、同步、并发以及它们在Java中的实现方式。Java作为一种广泛应用于分布式系统开发的编程语言,其内部...
AtomicInteger 类可以用于实现高效的线程同步机制。 等待和通知 等待和通知是多线程编程中的一种重要机制,用于让线程等待某个条件的出现或通知其他线程。Java 中提供了 wait() 方法和 notify() 方法来实现等待和...
线程池顶层实现原理是指 Java 中的线程池实现机制,该机制是基于线程模型、线程状态和执行流程这三个核心概念。 线程模型 在 Java 中,有两种类型的线程模型:用户线程(UTL)和内核线程(KTL)。用户线程是由应用...
1. **Synchronized关键字**:这是最基本的同步机制之一,可以通过同步方法或者同步代码块的方式来实现。当一个线程获得了锁之后,其他试图获取相同锁的线程将会被阻塞,直到当前线程释放锁。 ```java public ...
限流是一种服务降级机制,旨在限制系统的输入和输出流量,以达到保护系统的目的。下面将详细介绍限流的概念、实现方法和Java实现。 什么是限流 限流是服务降级的一种,旨在限制系统的输入和输出流量,以达到保护...
3.基于AtomicInteger的CAS机制; 4.使用Redis作为原子计数器(watch事务+decr操作),RabbitMQ作为消息队列记录用户抢购行为,MySQL做异步存储。 上述四个解决方案均使用了JMeter进行压力与性能测试(实验设置的是10...
Java Atomic类是Java并发编程中的一种机制,用于实现原子操作。Atomic类提供了一种无锁的方式来实现线程安全的操作。Atomic类使用CAS(Compare-And-Swap)操作来实现原子操作。CAS操作是一种原子操作,用于比较并...
3. **多线程管理**:引入Synchronous关键字、原子数(AtomicInteger)和线程池等机制,确保多线程环境下的资源安全访问和高效调度。 - **Synchronous**:保证数据访问的同步,防止多个线程同时修改同一数据导致的...