Netty的并发编程实践3:CAS指令和原子类
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗,因此这种同步被称为阻塞同步,它属于一种悲观的并发策略,我们称之为悲观锁。随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,被称为乐观锁。简单地说,就是先进行操作,操作完成之后再判断操作是否成功,是否有并发问题,如果有则进行失败补偿,如果没有就算操作成功,这样就从根本上避免了同步锁的弊端。
目前,在Java中应用最广泛的非阻塞同步就是CAS,在IA64、X86指令集中通过cmpxchg指令完成CAS功能,在sparc-TSO中由case指令完成,在ARM和PowerPC架构下,需要使用一对Idrex/strex指令完成。
从JDK1.5以后,可以使用CAS操作,该操作由sun.misc.Unsafe类里的compareAndSwapInt()和compareAndSwapLong()等方法包装提供。通常情况下sun.misc.Unsafe类对于开发者是不可见的,因此,JDK提供了很多CAS包装类简化开发者的使用,如AtomicInteger。
下面,结合Netty的源码,我们对原子类的正确使用进行详细说明。
打开ChannelOutboundBuffer的代码,看看如何对发送的总字节数进行计数和更新操作,先看定义,如图21-9所示。
图21-9 原子类的应用
首先定义了一个volatile的变量,它可以保证某个线程对于totalPendingSize的修改可以被其他线程立即访问,但是,它无法保证多线程并发修改的安全性。紧接着又定义了一个AtomicIntegerFieldUpdater类型的变量WTOTAL_PENDING_SIZE_UPDATER,实现totalPendingSize的原子更新,也就是保证totalPendingSize的多线程修改并发安全性,我们重点看AtomicIntegerFieldUpdater的API说明,如图21-10所示。
从API的说明可以看出,它主要用于实现volatile修饰的int变量的原子更新操作,对于使用者,必须通过类似compareAndSet或者set或者与这些操作等价的原子操作来保证更新的原子性,否则会导致问题。
图21-10 AtomicIntegerFieldUpdater Java DOC说明
继续看代码,当执行write操作外发消息的时候,需要对外发的消息字节数进行统计汇总。由于调用write操作的既可以是I/O线程,也可以是业务的线程,还可能由业务线程池多个工作线程同时执行发送任务,因此,统计操作是多线程并发的,这也就是为什么要将计数器定义成volatile并使用原子更新类进行原子操作。下面看计数的代码,如图21-11所示。
图21-11 通过自旋对计数器进行更新
首先,我们发现计数操作并没有使用锁,而是利用CAS自旋操作,通过TOTAL_ PENDING_SIZE_UPDATER.compareAndSet(this,oldValue, newWriteBufferSize)来判断本次原子操作是否成功。如果成功则退出循环,代码继续执行;如果失败,说明在本次操作的过程中计数器已经被其他线程更新成功,需要进入循环,首先对oldValue进行更新,代码如下。
oldValue= totalPendingSize;
然后重新对更新值进行计算。
newWriteBufferSize= oldValue + size;
继续循环进行CAS操作,直到成功。它跟AtomicInteger的compareAndSet操作类似。
使用Java自带的Atomic原子类,可以避免同步锁带来的并发访问性能降低的问题,减少犯错的机会。因此,Netty中对于int、long、boolean等成员变量大量使用其原子类,减少了锁的应用,从而降低了频繁使用同步锁带来的性能下降。
Netty的并发编程实践3:CAS指令和原子类的更多相关文章
- Netty的并发编程实践4:线程安全类的应用
在JDK1.5的发行版本中,Java平台新增了java.util.concurrent,这个包中提供了一系列的线程安全集合.容器和线程池,利用这些新的线程安全类可以极大地降低Java多线程编程的难度, ...
- Netty的并发编程实践2:volatile的正确使用
长久以来大家对于volatile如何正确使用有很多的争议,既便是一些经验丰富的Java设计师,对于volatile和多线程编程的认识仍然存在误区.其实,volatile的使用非常简单,只要理解了Jav ...
- Netty的并发编程实践1:正确使用锁
很多刚接触多线程编程的开发者,虽然意识到了并发访问可变变量需要加锁,但是对于锁的范围.加锁的时机和锁的协同缺乏认识,往往会导致出现一些问题.下面笔者就结合Netty的代码来讲解下这方面的知识. 打开F ...
- Netty的并发编程实践5:不要依赖线程优先级
当有多个线程同时运行的时候,由线程调度器来决定哪些线程运行.哪些等待以及线程切换的时间点,由于各个操作系统的线程调度器实现大相径庭,因此,依赖JDK自带的线程优先级来设置线程优先级策略的方法是错误和非 ...
- Java并发编程笔记1-竞争条件&初识原子类&可重入锁
我们知道,在多线程访问一个共享变量的时候会发生安全问题. 首先看下面例子: public class Counter { private int count; public void add(){ t ...
- java并发编程(十四)----(JUC原子类)对象的属性修改类型介绍
今天我们介绍原子类的最后一个类型--对象的属性修改类型: AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUp ...
- java并发编程(十二)----(JUC原子类)数组类型介绍
上一节我们介绍过三个基本类型的原子类,这次我们来看一下数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray.其中前两个的使用方 ...
- 并发编程实践五:ReentrantLock
ReentrantLock是一个可重入的相互排斥锁,实现了接口Lock,和synchronized相比,它们提供了同样的功能.但ReentrantLock使用更灵活.功能更强大,也更复杂.这篇文章将为 ...
- Java并发编程实践
最近阅读了<Java并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...
随机推荐
- python可用ORM之Pony
Pony是Python的一种ORM,它允许使用生成器表达式来构造查询,通过将生成器表达式的抽象语法树解析成SQL语句.它也有在线ER图编辑器可以帮助你创建Model. 示例分析 Pony语句: sel ...
- JasperReport报表开发(一)--原理介绍
1. JasperReport介绍 JasperReport 是一个开源的Java报表引擎,它不像其他的报表工具,例如Crystal报表是基于Java的,没有自己的表达式语法.Jasper Repor ...
- AutoDesk Forge 获取令牌认证
创建应用程序 在开始使用Forge Platform之前,您需要设置一个应用程序并获取您的客户端ID和密码. 步骤1:登录Dev Portal 去开发门户网站:https://developer.au ...
- 济南清北学堂游记 Day 0.
(摄于千佛山山顶,济南城区风光) 看似稳得一比,实则慌如老狗= = 我可能是报到最早的且实力最弱的一只. 早晨六点二十被从床上拉起来,然后在火车站附近匆忙吃了点东西就坐火车去济南了. 路途不算远,大概 ...
- POJ1509 Glass Beads [后缀自动机]
题意: 给一个字符串S,每次可以将它的第一个字符移到最后面,求这样能得到的字典序最小的字符串.输出开始下标 练习SAM第一题! SS构造SAM,然后从开始尽量走最小走n步就可以啦 什么?开始位置?!R ...
- 《算法导论》Chapter 4 Divide-and-Conquer的学习笔记
Introduction Divide-and-Conquer的三个步骤: Divide the problem into a number of subproblems that are small ...
- Go基础之--排序和查找操作
排序操作主要都在sort包中,导入就可以使用了import("sort") 常用的操作 sort.Ints:对整数进行排序sort.Strings:对字符串进行排序sort.Flo ...
- javascript 利用FileReader和滤镜上传图片预览
FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File或 Blob对象指定要读取的文件或数据. 1.FileReader接口的方法 Fi ...
- Navicat Mac 快捷键详解
Navicat 是数据库管理工具,满足了大家对数据库的存储过程.事件.触发器.函数.视图等功能,并且支持MySQL.MariaDB.SQL Server.SQLite.Oracle 和 Postgre ...
- Leetcode刷题C#版之Toeplitz Matrix
题目: Toeplitz Matrix A matrix is Toeplitz if every diagonal from top-left to bottom-right has the sam ...