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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 一个人的合唱

手写简易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问题。而版本号只要发生了修改都会增加而无论操作是什么。