分析:
如果是全局变量,i++是不安全的,因为java在操作i++的时候,是分步骤做的,可以理解为:
temp = i;
temp2 = i+1;
i=temp2;
如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的。
从更底层的角度讲,主要是因为i++这个操作不是原子性的,这个会编译成i= i +1; 所以会出现多线程访问冲突问题。volatile虽然可以保证多线程对修改可见,但代码中用到了
i++, 主要是i++不是原子性操作,这个会编译成i = i +1,其实是做了3个步骤,一个是读取,修改,写入 。所以会出现多线程访问冲突问题
解决方案:
在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而java.util.concurrent.AtomicInteger是一个提供原子操作的Integer类,其提供了线程安全且高效的原子操作,是线程安全的。
AtomicInteger类的底层实现原理是利用处理器的CAS操作(Compare And Swap,比较与交换,一种有名的无锁算法)来检测栈中的值是否被其他线程改变,如果被改变则CAS操作失败。这种实现方法在CPU指令级别实现了原子操作,因此,其比使用synchronized来实现同步效率更高。
CAS操作过程都包含三个运算符:内存地址V、期望值E、新值N。当操作的时候,如果地址V上存放的值等于期望值E,则将地址V上的值赋为新值N,否则,不做任何操作,但是要返回原值是多少。这就保证比较和设置这两个动作是原子操作。系统主要利用JNI(Java Native Interface,Java本地接口)来保证这个原子操作,它利用CPU硬件支持来完成,使用硬件提供swap和test_and_set指令,但CPU下同一指令的多个指令周期不可中断,SMP(Symmetric Multi-Processing)中通过锁总线支持这两个指令的原子性
示例代码:
package com.tree.blog.pool.cas;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicTest {
volatile int initialValue=1;
//Atomic包下的是基于CAS实现的,是线程安全的,不需要加锁
AtomicInteger initialAtomicValue=new AtomicInteger(1);
/**
* i++ 是线程不安去的
* 这个语句在执行的时候实际分为了三部
* 1、在栈中给一个 tp=i
* 2、执行i+1, tp2=i+1
* 3、赋值 给i完成,i=tp2
* 如果线程1在执行第一条代码的时候,线程2访问i变量,这个时候,i的值还没有变化,还是原来的值,所以是不安全的
* @param args
*/
public static void main(String[] args) {
AtomicTest test = new AtomicTest();
ExecutorService executorService = Executors.newFixedThreadPool(3000);
//使用Callable+future可以接受返回值其它的不行
Runnable run = test::increment;
Runnable runLock = test::incrementWithLock;
Runnable atomicRun = test::atomicIncrement;
//线程不安全,会出现多个线程得到相同的值
for (int i = 0; i <1000 ; i++) {
executorService.submit(run);
}
//同步锁,不会出粗,并发性能低
for (int i = 0; i <1000 ; i++) {
executorService.submit(runLock);
}
//使用基于CAS实现的原子操作,不错出现重复
for (int i = 0; i <1000 ; i++) {
executorService.submit(atomicRun);
}
executorService.shutdown();
}
/**
* 同步锁 执行自增
* @return
*/
public synchronized int incrementWithLock(){
System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialValue);
return initialValue++;
}
/**
* 执行自增
* @return
*/
public int increment(){
System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialValue);
return initialValue++;
}
/**
* 同步锁 执行自增
* @return
*/
public synchronized int atomicIncrement(){
System.out.println("线程名称:"+Thread.currentThread().getName()+"-当前值:"+initialAtomicValue.get());
return initialAtomicValue.getAndIncrement();
}
}
参考: