惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Hacker News
The Hacker News
Martin Fowler
Martin Fowler
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
U
Unit 42
F
Full Disclosure
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recorded Future
Recorded Future
Security Archives - TechRepublic
Security Archives - TechRepublic
阮一峰的网络日志
阮一峰的网络日志
T
Threatpost
P
Privacy International News Feed
GbyAI
GbyAI
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
I
Intezer
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
P
Privacy & Cybersecurity Law Blog
A
Arctic Wolf
博客园 - 聂微东
博客园 - 叶小钗
Cisco Talos Blog
Cisco Talos Blog
H
Help Net Security
S
Schneier on Security
Y
Y Combinator Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
T
The Exploit Database - CXSecurity.com
T
Tor Project blog
月光博客
月光博客
NISL@THU
NISL@THU
A
About on SuperTechFans
Spread Privacy
Spread Privacy
Blog — PlanetScale
Blog — PlanetScale
D
DataBreaches.Net
雷峰网
雷峰网
C
CXSECURITY Database RSS Feed - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 【当耐特】
G
Google Developers Blog
W
WeLiveSecurity
P
Palo Alto Networks Blog
The Last Watchdog
The Last Watchdog
K
Kaspersky official blog
博客园 - 司徒正美
L
LINUX DO - 热门话题
小众软件
小众软件

博客园 - 一个人的合唱

手写简易docker synchronized 泛型学习 反码与补码 解决128位秘钥长度限制的方法 CAP学习 ThreadLocal的学习 读书笔记脑图列表 a=a++和a=++a centos7下安装python3 深入理解jvm虚拟机笔记(二) volatile学习 echarts重新绘制的时候数据未更新 深入理解jvm虚拟机笔记(一) elasticsearch学习笔记 使用fiddler模拟post请求 二叉堆 二叉平衡树 二叉查找树
AtomicInteger的使用
一个人的合唱 · 2017-11-20 · via 博客园 - 一个人的合唱

AtomicInteger的使用

  在之前一篇volatile学习里面提到过了,volatile修饰的变量只是保证内存可见性,无法保证原子性,可能出现写冲突。要想保证线程安全,需要使用AtomicInteger。具体代码如下:

public class AtomicTest {
    public static AtomicInteger race=new AtomicInteger(0);
    public static void increase(){
        race.incrementAndGet();
    }
    private static final int THREADS_COUNT=20;
    public static void main(String[] args){
        Thread[] threads=new Thread[THREADS_COUNT];
        for(int i=0;i<THREADS_COUNT;i++){
            threads[i]=new Thread(new Runnable() {
                @Override
                public void run() {
                    for(int i=0;i<10000;i++){
                        increase();
                    }
                }
            });
            threads[i].start();
        }
        while(Thread.activeCount()>1){
            Thread.yield();
            System.out.println(race);
        }
    }
}

运行结果为200000,若是把变量用volatile修饰,然后increase方法替换成race++,那么最后得到的结果基本不为200000,且每一次得到的结果都可能是不同的。AtomicInteger的incrementAndGet是通过unsafe的getAndAddInt来实现的,而后者的代码如下:

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

  其中var1是调用unsafe的AtomicInteger对象,而var2是用来记录value本身在内存的编译地址的,这个记录,也主要是为了在更新操作在内存中找到value的位置,方便比较。而var5则是AtomicInteger的value值,被valotile修饰,当冲突时因为内存可见性,操作失败的线程可以看到修改成功的线程修改后的最新值。compareAndSwapInt亦即CAS,是原子操作,它的作用是若原值为var5即在这之间没发生过修改,那么设置为新值var5 + var4。AtomicInteger方法不是互斥同步的,而是非阻塞同步,也就是乐观锁,而前者被称为悲观锁。

  悲观锁认为冲突经常会发生,所以直接上锁,强制所有线程一次有且只有一条线程能操作,严格保证串行性。而乐观锁认为冲突很少发生,先进行操作,之后检查有没有冲突,若发生了冲突再采取其他补偿措施(最常见的补偿措施就是不断地重试,直到成功为止)。两者并无优劣之分,使用场景不同而已。若是冲突经常发生,那么采用乐观锁场景中,每次操作只有一条线程成功,失败的线程会不断尝试然后不断冲突,性能可能很差,但若是冲突很少发生,那么悲观锁那样一次只允许一条线程读写那么效率就太低了。乐观锁是通过检查版本号来检测是否发生冲突的,而版本号每次修改成功时+1。你每次需要修改值时带上版本号,若是你的版本号与当前一致那么修改成功,若是中间被其他线程修改了,那么版本号就与你的不一样了,你只能请求最新的版本号然后尝试着再次修改直到成功为止。像版本号这样单向增加的比较好,若是用原值来作为比较的标准的话,有可能会发生中间有人把原值加1另外一人把原值减1,然后轮到你的时候你以为原值在这中间没有变化而可能产生歧义,亦即ABA问题。而版本号只要发生了修改都会增加而无论操作是什么。