本文转载自微信公众号「安琪拉的高并博客」,作者安琪拉。发系转载本文请联系安琪拉的线程性博客公众号。 大家好,安全我是原性安琪拉,这是高并并发编程的第五集,完整大纲如下: 面试官:你好,发系你先自我介绍一下吧。线程性 安琪拉:面试官你好,安全我是原性草丛三婊,最强中单,高并火球拥有者、发系不焚者,线程性安琪拉,安全这是原性我的简历,请过目。 面试官:听前一个面试官说你Java并发这块掌握的不错,我们深入的交流一下; 安琪拉:好好好,可以交流的深入一点 面试官:什么是线程安全性? 安琪拉:这个问题第一次被问,但是个好问题。 当多个线程访问某个类时,香港云服务器不管运行环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的协同或者同步,这个类都能表现出正确的行为,那么这个类是线程安全的。 面试官:线程安全性有哪三大特点? 或者说线程不安全是由于什么引起的? 安琪拉:【太老套了吧,能不能来点新的】 线程不安全的原因: 当前的一个操作可能不是原子的,执行过程中会被打断,其他线程有能力修改共享变量的值,同时存在线程修改的值不是立即对其他线程可见的,因为线程有自己的执行空间,另外一点就是存在程序可能存在乱序执行的情况,单线程没问题,但是多个线程同时执行,线程共享的网站模板数据会出现错乱,以上说的自己问题归纳出线程安全需要保证的三个特性: 提供互斥访问、同一时刻只能有一个线程在操作 一个线程对主内存的修改可以及时地被其他线程看到 有序性是指程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的。(你可能会想难道还有不一致的,是的,因为存在指令重排序,为什么会有指令重排,因为性能优化的需要,比如把多次访问主存合并到一起执行比计算和访问主存交替访问更高效),重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。 面试官:那你用过java.util.concurrent.atomic包下原子性相关的类吗? 安琪拉:用过的,Java提供了很多AtomicXXX相关的原子类,如下图所示: 面试官:能举个例子说明下用法吗? 安琪拉:比如存在并发,源码下载计数的场景,以netty为例,它的线程池工厂类如下: nextId就是 AtomicInteger类型的。每次创建线程给线程命名的时候, 代码如下: 通过 incrementAndGet 实现原子性的 +1。 面试官:如果不用AtomicInteger,就用普通的int 会有什么后果? 安琪拉:首先我们知道 +1 操作不是原子性的,可以分成这么几条指令:取数指令,将数据压入操作数栈,执行+1操作,赋值。 关于指令这块,扔个蓝。我们编译一段Java code 看一下。 代码和字节码指令分别为: 指令,对应的操作解释也有,如下: 写这么多就是为了让大家明白 a += 1 这种操作它不是原子的,是有多条指令组成,真的不容易,快给我点个赞,好心人的蓝buff 面试官:那能跟我讲下Atomic 的实现原理吗? 安琪拉:【要开始卷了,到安琪拉最爱的源码环节】 代码很短,注释就一句话,原子性的增加当前值。 继续下探: 入参是三个值:var1、var2、var4 ,我们先看下这三个值分别是什么? Val1:this ,也就是AtomicInteger 对象nextId Val2:valueOffset 看下代码,我另外画了个图,我们知道一个对象存储空间由对象头和成员变量组成的,那valueOffset 就是成员变量value 在AtomicInteger 对象中的偏移量。 初学者可能会问,函数放在哪呢?函数都放在方法区,因为是属于类的,不是对象私有的。 Val4:1 那开始详细解释下,下面这段代码: compareAndSwapInt 方法:比较val1(AtomicInteger对象)的var2(valueOffset偏移量)的值与var5(原始值)是否相等,如果相等,让值更新成var5(原始值) + val4(1) compareAndSwapInt 就是Java中非常重要,也是非常出名的CAS操作,比较并交换,并发底层框架用到的地方很多。 compareAndSwapInt 会返回CAS支持状态,如果执行失败,会循环执行,直到成功。 失败的原因一般是同时有别的线程修改了这个变量的值,所以比较的时候不相等,下次执行会获取最新值执行CAS。 。。。。嘤嘤嘤,打字好累啊,先写到这,要去吃自助餐了,明天再写可见性和有序性。