互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗,因此这种同步被称为阻塞同步,它属于一种悲观的并发策略,我们称之为悲观锁。随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,被称为乐观锁。简单地说,就是先进行操作,操作完成之后再判断操作是否成功,是否有并发问题,如果有则进行失败补偿,如果没有就算操作成功,这样就从根本上避免了同步锁的弊端。

目前,在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指令和原子类的更多相关文章

  1. Netty的并发编程实践4:线程安全类的应用

    在JDK1.5的发行版本中,Java平台新增了java.util.concurrent,这个包中提供了一系列的线程安全集合.容器和线程池,利用这些新的线程安全类可以极大地降低Java多线程编程的难度, ...

  2. Netty的并发编程实践2:volatile的正确使用

    长久以来大家对于volatile如何正确使用有很多的争议,既便是一些经验丰富的Java设计师,对于volatile和多线程编程的认识仍然存在误区.其实,volatile的使用非常简单,只要理解了Jav ...

  3. Netty的并发编程实践1:正确使用锁

    很多刚接触多线程编程的开发者,虽然意识到了并发访问可变变量需要加锁,但是对于锁的范围.加锁的时机和锁的协同缺乏认识,往往会导致出现一些问题.下面笔者就结合Netty的代码来讲解下这方面的知识. 打开F ...

  4. Netty的并发编程实践5:不要依赖线程优先级

    当有多个线程同时运行的时候,由线程调度器来决定哪些线程运行.哪些等待以及线程切换的时间点,由于各个操作系统的线程调度器实现大相径庭,因此,依赖JDK自带的线程优先级来设置线程优先级策略的方法是错误和非 ...

  5. Java并发编程笔记1-竞争条件&初识原子类&可重入锁

    我们知道,在多线程访问一个共享变量的时候会发生安全问题. 首先看下面例子: public class Counter { private int count; public void add(){ t ...

  6. java并发编程(十四)----(JUC原子类)对象的属性修改类型介绍

    今天我们介绍原子类的最后一个类型--对象的属性修改类型: AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUp ...

  7. java并发编程(十二)----(JUC原子类)数组类型介绍

    上一节我们介绍过三个基本类型的原子类,这次我们来看一下数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray.其中前两个的使用方 ...

  8. 并发编程实践五:ReentrantLock

    ReentrantLock是一个可重入的相互排斥锁,实现了接口Lock,和synchronized相比,它们提供了同样的功能.但ReentrantLock使用更灵活.功能更强大,也更复杂.这篇文章将为 ...

  9. Java并发编程实践

    最近阅读了<Java并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...

随机推荐

  1. Jmeter_脚本参数化与内存溢出的解决方案

    这一篇文章随便说一下JMETER的脚本参数化 1:Parameters的两种参数化方法 1.1:函数助手参数化 首先准备你的参数数据.我在bin/data中新建了一个dat文件,记事本另存为修改编码为 ...

  2. centos 6.5静态网址简单配置

    详细配置参数参考如下: DEVICE=eth0HWADDR=08:00:27:F1:72:C9TYPE=EthernetUUID=dab98425-fa46-4a5b-8f47-cf846d4acb3 ...

  3. python多线程限制并发数示例

    #coding: utf-8 #!/usr/bin/env python import Queue import threading import time prolock = threading.L ...

  4. ansible基础及使用示例

    1 介绍 Ansible 是一个系统自动化工具,用来做系统配管理,批量对远程主机执行操作指令. 2 实验环境 ip 角色 192.168.40.71 ansible管控端 192.168.40.72 ...

  5. iOS-xcode代码统计

    作为开发者,想不想知道自己写了多少行代码吗,打开终端,进入项目文件夹,然后进入想统计的某个文件夹,也可以直接在当前项目文件夹,然后终端输入下面的代码就可以了 find . "(" ...

  6. 深入理解Java内存模型之系列篇[转]

    原文链接:http://blog.csdn.net/ccit0519/article/details/11241403 深入理解Java内存模型(一)——基础 并发编程模型的分类 在并发编程中,我们需 ...

  7. 洛谷 [P1129] [ZJOI2007] 矩阵游戏

    这竟然是一道二分图 乍一看,可能是用搜索做,但是这个数据范围,一定会T. 我们观察发现,无论怎样变换,同一行的一定在同一行,同一列的一定还在同一列.所以说,一行只能配一列.这样,我们的目标就是寻找是否 ...

  8. Vue中结合Flask与Node.JS的异步加载功能实现文章的分页效果

    你好!欢迎阅读我的博文,你可以跳转到我的个人博客网站,会有更好的排版效果和功能. 此外,本篇博文为本人Pushy原创,如需转载请注明出处:http://blog.pushy.site/posts/15 ...

  9. AOF持久化

    AOF持久化是通话记录执行的命令进行存储的 AOF持久化的事先可以分为 (1)文件追加,新写入的命令都会被追加到redisserver的aof_buf缓冲区里面. 在服务器每一次执行事件循环的时候都会 ...

  10. 【笔记】h5 页面唤起电话呼叫

    参考文章:https://www.cnblogs.com/lilin1995/p/5640684.html 最近完成一个公司的官网移动端页面,涉及到了唤起电话这个功能,说实在js 并没有为此提供 ap ...