volatile为什么不保证原子性?
**number++
操作对应的字节码
:
number++ 被拆分成3个指令
- 执行 GETFIELD 拿到主内存中的原始值
number
[ ** ]- 执行 IADD 进行加
1
操作- 执行 PUTFIELD 把工作内存中的值写回主内存中 [ 会造成给主内存重复覆盖相同值 ]
当多个线程并发执行 PUTFIELD
指令的时候,会出现写回主内存覆盖问题,故volatile不能保证原子性。
请你使用代码验证volatile不保证原子性?
public class VolatileThread extends Thread{
//1.定义volatile 可见性count
private volatile int count=0;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
count++;
System.out.println("count =====>"+count);
}
}
}
public class DemoTest {
public static void main(String[] args) {
VolatileThread volatilethread =new VolatileThread();
//2.定义100个线程计算count,按道理count被自增至10000
for (int i = 0; i < 100; i++) {
new Thread(volatilethread).start();
//new VolatileThread().start(); 注意二者区别
}
}
}
解决volatile不保证原子性问题
1.方法前加 synchronized 解决 [ 悲观锁 ]
public class VolatileThread extends Thread{
private volatile int count=0;
Lock lock = new ReentrantLock();
@Override
public synchronized void run() { //方法上加 synchronized
for (int i = 0; i < 100; i++) {
count++;
System.out.println("count =====>"+count);
}
}
}
2.加锁解决
public class VolatileThread extends Thread{
private volatile int count=0;
Lock lock = new ReentrantLock(); //1.定义 Lock 对象
@Override
public void run() {
lock.lock(); //2.加锁
for (int i = 0; i < 100; i++) {
count++;
System.out.println("count =====>"+count);
}
lock.unlock(); //3.解锁
}
}
3.原子类CAS机制解决 [ 乐观锁 ]
public class VolatileThread extends Thread{
private AtomicInteger atomicNumber = new AtomicInteger();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("count =====>"+(atomicNumber.getAndIncrement()+1));
}
}
}
CAS与Synchronized 比较:乐观锁,悲观锁。
CAS
和Synchronized
都可以保证多线程环境下共享数据的安全性。那么他们两者有什么区别?
Synchronized
是从悲观的角度出发(悲观锁
)
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。因此Synchronized我们也将其称之为悲观锁。jdk
中的ReentrantLock
也是一种悲观锁。性能较差!!
CAS
是从乐观的角度出发 (乐观锁)
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。CAS这种机制我们也可以将其称之为乐观锁。综合性能较好!