面试官:AtomicInteger是如何保证线程安全?

c6a99d046d28ed4719ef4cadbfaf83fe.png

blog.csdn.net/nanhuaibeian/article/details/120936139

一、为什么引入 AtomicInteger ?

谈到线程安全,会首先想到了synchronized 和 Lock,但是这种方式又有一个名字,叫做互斥锁,一次只能有一个持有锁的线程进入,再加上还有不同线程争夺锁这个机制,效率比较低,所以又称 悲观锁

与之相对应,就有了 乐观锁 的概念:它不加锁去完成某项操作,如果因为冲突失败就重试,直到成功为止。

AtomicInteger 保证线程安全就是使用了乐观锁,所以相对于悲观锁,效率更高。

在有多个线程同时使用CAS操作一个变量时,只有一个会胜出并成功更新,其余均会失败。失败的线程不会被挂起,仅被告知失败,并且允许再次尝试,当然,也允许失败的线程放弃操作

二、AtomicInteger 原理分析

1. 具体使用

假如我们想实现一个功能来统计网页访问量,可以使用 count++ 来统计访问量,但是这个自增操作不是线程安全的。

加锁实现:

class Counter {private volatile int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}

AtomicInteger 实现:

class Counter {//使用AtomicInteger之后,不需要加锁,也可以实现线程安全。private AtomicInteger count = new AtomicInteger();public void increment() {count.incrementAndGet();}public int getCount() {return count.get();}
}

2. 原理分析

AtomicInteger 类中定义的属性:

// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}

Unsafe是JDK内部的工具类,主要实现了平台相关的操作

sun.misc.Unsafe 是JDK内部用的工具类。它通过暴露一些Java意义上说“不安全”的功能给Java层代码,来让JDK能够更多的使用Java代码来实现一些原本是平台相关的、需要使用native语言(例如C或C++)才可以实现的功能。该类不应该在JDK核心类库之外使用。

简单来说:这段代码是为了获取value在堆内存中的偏移量

Value的定义:

private volatile int value;

volatile 主要特性有两点:防止重排序;实现内存可见性

内存可见性的作用是当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值

用CAS操作实现原子性:

AtomicInteger中有很多方法,例如

  • incrementAndGet() 相当于i++ ;

  • getAndAdd() 相当于i+=n 。

从源码中我们可以看出这几种方法的实现很相似,这里主要分析incrementAndGet()方法的源码

public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}}public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

incrementAndGet() 方法实现了自增的操作。

核心实现是先获取当前值和目标值(也就是current +1),如果compareAndSet(current, next) 返回成功则该方法返回目标值。

那么compareAndSet是做CAS操作

每次从内存中根据内存偏移量(valueOffset)取出数据,将取出的值跟expect 比较,如果数据一致就把内存中的值改为update,如果数据不一致说明内存中的数据已经有过更新,此时就进行回滚(expect值不生效)操作。

这样使用CAS的方法就保证了原子操作

Java中的 AtomicLong、AtomicBoolean 等方法的基本原理和思想跟AtomicInteger基本相同

三、总结

(1)用volatile关键字修饰 value 字段,保证了 value 字段对各个线程的可见性,各线程读取value字段时,会先从主从把数据同步到工作内存中,这样保证可见性

(2)Unsafe 实现操作原子性,用户在使用时无需增加额外的同步操作,compareAndSetInt方法是一个CAS操作,用native关键字修饰

原理:先比较内存中的值与expected是否一致,一致的前提下才赋予新的值x,此时返回true,否则返回false

a57fff809d8de0b4c962e9d3fc5b1ef1.gif

往期推荐

029ec2d876b52cfa74cbdcd599d84d1e.png

如何保证数据库和缓存双写一致性?


002829ded9a1ca06ffb653da6fc2646e.png

Spring官方推荐的@Transactional还能导致生产事故?


fee67f74ae465cc1e3ec648b7126a950.png

MySQL 精选 60 道面试题(含答案)


29c475861036d01135c944e8ccb58ac4.gif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544232.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习 训练验证测试_测试前验证| 机器学习

机器学习 训练验证测试In my previous article, we have discussed about the need to train and test our model and we wrote a code to split the given data into training and test sets. 在上一篇文章中,我们讨论了训练和测试模型的必要性,并编写了…

如何判断线程池已经执行完所有任务了?

作者 | 磊哥来源 | Java面试真题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)很多场景下,我们需要等待线程池的所有任务都执行完,然后再进行下一步操作。对于线程 Thread …

IRCTC的完整形式是什么?

IRCTC:印度铁路餐饮和旅游公司 (IRCTC: Indian Railways Catering and Tourism Corporation) IRCTC is an abbreviation of Indian Railways Catering and Tourism Corporation. It is a subsidiary of the Indian Railway established by the Ministry of Railways…

分布式锁的 3 种实现方案!

前言 大家好,我是磊哥。今天跟大家探讨一下分布式锁的设计与实现。希望对大家有帮助,如果有不正确的地方,欢迎指出,一起学习,一起进步哈~分布式锁概述数据库分布式锁Redis分布式锁Zookeeper分布式锁三种分布式锁对比1.…

java学习笔记16--异常

java学习笔记16--异常 异常 异常时导致程序中断运行的一种指令流,如果不对异常进行正确的处理,则可能导致程序的中断执行,造成不必要的损失, 所以在程序的设计中必须要考虑各种异常的发生,并正确的做好相应的处理&am…

ruby hash添加数据_如何在Ruby中向Hash添加元素?

ruby hash添加数据Before going through the ways to add elements to the hash instances, let us understand what could be called as a hash element. So, Hash is the collection of keys and their values. For example, 在介绍向哈希实例添加元素的方法之前,…

线程安全问题的 3 种解决方案!

作者 | 磊哥来源 | Java面试真题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)线程安全是指某个方法或某段代码,在多线程中能够正确的执行,不会出现数据不一致或数据污染的…

黑色30s高并发IIS设置

在这篇博文中,我们抛开对阿里云的怀疑,完全从ASP.NET的角度进行分析,看能不能找到针对问题现象的更合理的解释。 “黑色30秒”问题现象的主要特征是:排队的请求(Requests Queued)突增,到达HTTP.…

我们可以覆盖Java中的main()方法吗?

The question is that "Can we override main() method in Java?" 问题是“我们可以覆盖Java中的main()方法吗?” No, we cant override the main() method in java. 不,我们不能覆盖java中的main()方法 。 First, we will understand what …

一文读懂MySQL查询语句的执行过程

需要从数据库检索某些符合要求的数据,我们很容易写出 Select A B C FROM T WHERE ID XX 这样的SQL,那么当我们向数据库发送这样一个请求时,数据库到底做了什么?我们今天以MYSQL为例,揭示一下MySQL数据库的查询过程&a…

angularJS的$http.post请求,.net后台接收不到参数值的解决方案

JS通用部分var shoppingCartModule angular.module(starter, [ionic], function ($httpProvider) {// Use x-www-form-urlencoded Content-Type$httpProvider.defaults.headers.post[Content-Type] application/x-www-form-urlencoded;charsetutf-8;/*** The workhorse; conve…

带有示例的Python列表reverse()方法

列出reverse()方法 (List reverse() Method) reverse() method is used to reverse the elements of the list, the method is called with this list (list in which we have to reverse the elements) and it reverses all elements in the list. reverse()方法用于反转列表中…

复杂度O(n)倒转链表

1 public class ListNode {2 int val;3 ListNode next;4 ListNode(int x) { val x; }5 ListNode(){}6 7 public static ListNode revese(ListNode input)8 {9 ListNode head new ListNode();//头插法的头 10 ListNode cur in…

synchronized底层是如何实现的?

作者 | 磊哥来源 | Java面试真题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)想了解 synchronized 是如何运行的?就要先搞清楚 synchronized 是如何实现?synchronized 同步…

java sublist_Java Vector subList()方法与示例

java sublist向量类subList()方法 (Vector Class subList() method) subList() method is available in java.util package. subList()方法在java.util包中可用。 subList() method is used to return a set of sublist [it returns all those elements exists in a given rang…

单例模式 4 种经典实现方法

0.前言 如果你去问一个写过几年代码的程序员用过哪些设计模式,我打赌,90%以上的回答里面会带【单例模式】。甚至有的面试官会直接问:说一下你用过哪些设计模式,单例就不用说了。你看,连面试官都听烦了,火爆…

CSRF简单介绍及利用方法-跨站请求伪造

0x00 简要介绍 CSRF(Cross-site request forgery)跨站请求伪造,由于目标站无token/referer限制,导致攻击者可以用户的身份完成操作达到各种目的。根据HTTP请求方式,CSRF利用方式可分为两种。 0x01 GET类型的CSRF 这种类…

java setsize_Java Vector setSize()方法与示例

java setsize向量类setSize()方法 (Vector Class setSize() method) setSize() method is available in java.util package. setSize()方法在java.util包中可用。 setSize() method is used to set the new size of this vector and when new size (n_size) > current size …

虾皮二面:什么是零拷贝?如何实现零拷贝?

前言 零拷贝是老生常谈的问题啦,大厂非常喜欢问。比如Kafka为什么快,RocketMQ为什么快等,都涉及到零拷贝知识点。最近技术讨论群几个伙伴分享了阿里、虾皮的面试真题,也都涉及到零拷贝。因此本文将跟大家一起来学习零拷贝原理。1.…

设计模式2:工程模式(1)

什么是工厂模式? 提供一个创建一系列或相互依赖对象的接口,而不需指定它们具体的类。 通俗的讲就是定义了多个产品的类,且只有一个工厂类,而这个工厂类根据需求的不同,可以产生不同产品类的对象。 作用:主要为创建对象提供过度接…