多线程下 i++和++i 操作如何保证线程安全

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hefenglian/article/details/82586243

volatile解决的是多线程间共享变量的可见性问题,而保证不了多线程间共享变量原子性问题。
对于多线程的i++,++i,依然还是会存在多线程问题,volatile是无法解决的

比如:变量i=0,A线程更新i+1,B线程也更新i+1,经过2次自增操作之后,i可能不等于2,而是等于1;

原因是i++和++i并非原子操作,我们通过javaP查看字节码,会发现

void f1() { i++; } 

的字节码‘如下:

    void f1();  
    Code:  
    0: aload_0  
    1: dup  
    2: getfield #2; //Field i:I  
    5: iconst_1  
    6: iadd  
    7: putfield #2; //Field i:I  
    10: return 

可见i++执行了多部操作,从变量i中读取读取i的值->值+1 ->将+1后的值写回i中,这样在多线程的时候执行情况就类似如下了

 Thread1             Thread2  
    r1 = i;             r3 = i;         //读取i值        
    r2 = r1 + 1;        r4 = r3 + 1;    //i值加1
    i = r2;             i = r4;         //写回到i

这样会造成的问题就是 r1, r3读到的值都是 0,最后两个线程都将 1 写入 i, 最后 i等于 1,但是却进行了两次自增操作。

可知加了volatile和没加volatile都无法解决非原子操作的线程同步问题。

解决方法

1、 使用循环CAS,实现i++原子操作
Java从JDK1.5开始提供了java.util.concurrent.atomic包来提供线程安全的原子操作类。这些原子操作类都是是用CAS来实现,i++的原子性操作。以AtomicInteger为例子,讲一下 public final int getAndIncrement(){} 方法的实现。
Atomic包参考 https://www.cnblogs.com/chenpi/p/5375805.html

public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

2、使用锁机制,实现i++原子操作

// 使用Lock实现,多线程的数据同步
    public static ReentrantLock lock = new ReentrantLock();

3、使用synchronized,实现i++原子操作

for (int i = 0; i < times; i++) {
            // 进行自加的操作
            synchronized (SynchronizedTest.class) {
                count++;
            }

}

参考 https://blog.csdn.net/zbw18297786698/article/details/53420780

猜你喜欢

转载自blog.csdn.net/hefenglian/article/details/82586243