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并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...
随机推荐
- SQL Server中计算表达式的和
项目使用的是SQL Server数据库,需要做一个审核规则,字段A中表达式的值和字段B中的值,做比较: 需求本身很简单,但是表达式中存在很多非法字符(非法全角,运算符,汉字--) eg:1.1.1*2 ...
- windows下安装配置python + selenium 来驱动firefox
第一步,首先下载安装python ,我下载的是3.5版本,这个版本,自带了pip工具,不需要安装pip了 :) 链接地址:python 3.5 第二步,执行pip install selenium 安 ...
- POJ [P2289] Jamie's Contact Groups
二分+二分图多重匹配 辣鸡ACM式读入 对于这种奇葩的读入方法,还是老老实实的用scanf吧 #include <iostream> #include <cstdio> #in ...
- Sql2012数据库还原
Sql2012数据库还原(通过.bak数据库备份文件) 昨天系统挂了,那叫一个悲惨,重装了系统,但是sql2012的数据没有备份,同事帮忙发来备份文件(.bak),开始还原数据. 步骤:1 自己新建一 ...
- Azure Automation (5) 调整Azure SQL Database DTU
<Windows Azure Platform 系列文章目录> 之前有客户提了需求,在9点以后,把Azure SQL Database DTU提高 在凌晨,把Azure SQL Datab ...
- 小甲鱼OD学习第6讲
这次我们的任务是破解这个通讯录的软件,首先,我们在通讯录一个分组添加第5个人,发现弹出对话框,限制每组只能添加4个人 并且发现最多只能添加3个分组 我们把程序载入OD,运行,添加人,这个时候点击暂停, ...
- 汽车之家店铺数据抓取 DotnetSpider实战[一]
一.背景 春节也不能闲着,一直想学一下爬虫怎么玩,网上搜了一大堆,大多都是Python的,大家也比较活跃,文章也比较多,找了一圈,发现园子里面有个大神开发了一个DotNetSpider的开源库,很值得 ...
- ES6 学习笔记之一 块作用域与let和const
---恢复内容开始--- 在学习ES6的块作用域和 let.const 之前,我们先来看看ES5以前的 var 关键字. var 关键字用于定义一个变量,通常我们会将其与变量的赋值合并为一条语句,就像 ...
- qt中的多线程
1.dialog.h #define DIALOG_H #include <QDialog>#include"mythread.h"namespace Ui {clas ...
- Struts2 中添加 Servlet
Struts2中如何添加Servlet 以前Java开发都是Servlet的天下,如今是各种框架横行,遇到一个需要将以前的Servlet加入到现有的Struts2的环境中. Google之后发现Sta ...