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。

但是,看下面的实际运行的结果图。

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

可以看到,1 没有输出,但是2 输出了2次,

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

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

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

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


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

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

尴尬啦,

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

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


2018-01-10,更新如下;

现在找到怎么解决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。

你可能感兴趣的:(java,多线程)