i++不是原子操作,看似简单,实则巨坑的一个线程同步的问题。synchronized 和 volatile

本文章转载自李学凯的博客,若侵权请告知,立即删除。

线程同步,即对多个线程可能同时访问一个资源的时候。这个时候,有个互斥的要求,一般都是加锁。synchronized。但是,有时候,仅仅这个synchronized是不够用的,还可能会使用到一个不常用的关键字:volatile
下面看看这个简单的多线程同步的代码。看看加不加这个volatile会有什么不同的效果。

package com.lxk.threadTest.mianShiTest.staticAttribute;  

/** 
 * @author lxk on 2017/11/17 
 */  
public class MyThread implements Runnable {  

    static Integer i = 0;  

    @Override  
    public void run() {  
        while (true) {  
            synchronized (i) {  
                if (i < 100) {  
                    i++;  
                    String currentThreadName = Thread.currentThread().getName();  
                    System.out.println(currentThreadName + " i = " + i);  
                } else {  
                    break;  
                }  
            }  
        }  

    }  

}  

然后就是main方法。

package com.lxk.threadTest.mianShiTest.staticAttribute;  

/** 
 * 首先是2个线程一起执行,再有就是i++他不是原子操作。 
 * 
 * @author lxk on 2017/11/17 
 */  
public class Main {  
    public static void main(String[] args) {  
        Thread t1 = new Thread(new MyThread());  
        Thread t2 = new Thread(new MyThread());  
        t1.start();  
        t2.start();  
    }  
}  

先是实现多线程同步的代码,直接在main方法里面,new两个线程,启动,就没有然后啦。简单看完之后,有什么想法。可能如下:
2个线程,对静态变量i加锁,说明2个线程加锁的对象是统一的,加锁的是一个东西。保证每次操作的时候,都是线程安全的。。。

所以,猜测的运行结果:

线程0和线程1,两个线程,会输出1-100的数,因CPU执行权的问题,说不好谁先谁后,但是,理论上讲应该是2个线程共同执行,输出1-100。

但是,看下面的实际运行的结果图。
这里写图片描述
可以看到,1 没有输出,但是2 输出了2次,

对,没错,这个是有随机性的,因为每个人的电脑不同,性能也不同,所以,不一定 会出现这个现象。

但是,我这真出现这个现象啦,这就是线程不安全啦。

正因为这个不确定性,所以,多线程同步的问题,就是个很大的问题。所以,多线程编程,才会那么重要。

因为,这个bug不是你想复现就立马能复现的。

我原以为是给这个静态变量 i 加上 volatile ,问题就算完事了,但是,好像,不是那么回事呐。还是有问题,还是线程不安全。
尴尬啦,

这个问题,暂时没解决呢。

容我先把这个问题留下,但是这个多线程同步的问题,确实厉害,你要是不同步这个 i ++ ,那就不会有这么多事。

2018-01-10,更新如下;

现在找到怎么解决i++不是原子操作的方法啦。

使用getAndIncrement(),相当于i++

public class MyThread implements Runnable {  

    private static AtomicInteger i = new AtomicInteger(0);  

    @Override  
    public void run() {  
        while (true) {  
            synchronized (i) {  
                if (i.get() < 100) {  
                    //相当于i++  
                    i.getAndIncrement();  
                    String currentThreadName = Thread.currentThread().getName();  
                    System.out.println(currentThreadName + " i = " + i);  
                } else {  
                    break;  
                }  
            }  
        }  

    }  

}  

这个是线程安全的integer。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值