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