案例一
下面代码在一百个线程里面对同一个变量执行相加操作,100个线程每个线程循环执行10000次,正常值为:一百万,如果对变量不加锁的情况下,则计算出来的值很容易不是一百万
package cn.sensorsdata.exercise.concurrentThread;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadMultiple {
// public static AtomicInteger atomicInteger = new AtomicInteger();
public static Integer atomicInteger =0;
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
CountDownLatch countDownLatch = new CountDownLatch(100);
for(int j=0;j< threads.length;j++){
threads[j] = new Thread(()->{
for(int i=0;i<10000;i++){
atomicInteger+=1;
// atomicInteger.incrementAndGet();
}
countDownLatch.countDown();
});
}
//流式启动
Arrays.stream(threads).forEach(x->x.start());
countDownLatch.await();
System.out.println("得到的值为"+atomicInteger);
}
}
//得到的值为203879
在对做加法的变量使用加锁时得出的值如下
package cn.sensorsdata.exercise.concurrentThread;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadMultiple {
public static AtomicInteger atomicInteger = new AtomicInteger();
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
CountDownLatch countDownLatch = new CountDownLatch(100);
for(int j=0;j< threads.length;j++){
threads[j] = new Thread(()->{
for(int i=0;i<10000;i++){
atomicInteger.incrementAndGet();
}
countDownLatch.countDown();
});
}
//流式启动
Arrays.stream(threads).forEach(x->x.start());
countDownLatch.await();
System.out.println("得到的值为"+atomicInteger);
}
}
//得到的值为1000000
图一:图一里面存在多个线程操作同一个公共变量的情况,当某一个线程读取到变量值后,还没有来的及将计算好的值回写到内存里,其他线程基于同一个值进行各自计算,造成数据被多个线程同时访问计算的情况。无法保证执行的顺序性。因此计算的值存在问题
图二:AtomicInteger内部实现加锁操作,保证了变量执行的原子性操作。
具体的底层实现:在底层的汇编层面在cpu阶段,会判断当前处理器是不是Multiple processor如果是多核处理器,则会个处理头加上Lock_IF_MP()如果是多处理器则加锁,
cpu具体的指令是 lock cmpxchg,但是cmpxchg这个指令并不是原子操作,也会引起值变化,于是在底层指令上加lock命令,在有线程进行cmpxchg的时候 直接锁住总线,其他线程无法操作,于是无法操作。
总结:在同一个进程里面,进程的公共变量时共享的,这个时候就需要考虑在并行处理下,赋值的问题,采取什么样的方式进行加锁