volatile,原子变量和ThreadLocal

概念

多线程中的变量

  首先我介绍的是volatile关键字,其次是原子变量,最后则是ThreadLocal线程本地变量

java基本内存模型

  用到volatile这个关键字以及后面的原子变量之前,我们必须先了解一下什么是java基本内存模型。
  先明确几个概念:
  主内存:主内存就是所有线程共享的内存,对于一个共享变量来说,主内存存放其真实数据(本尊数据)
  线程工作内存:线程对数据操作时,都会有自己的工作内存,对共享变量操作前,会先从主内存中获取到值,操作完后在回写回去。
  

volatile

  现在有2个线程A,B,他们要主内存中间的一个变量s=0;此时A线程要修改这个共享变量,它是先获取到这个值复制到线程工作内存里面去,然后在线程工作内存里面把这个值修改了,然后把这个值再写到主内存里面去。此时B读取这个s变量,那么值可能是0,也可能是线程A所修改的值。
  使用volatile这个关键字可以避免上述这种情况(使用锁来对变量加锁或者synchronized开销太大)。
  针对上述的例子,volatile的可见性保证了不会出现上述问题。
  什么是可见性呢?
  当一个线程修改了变量的值,新的值会立刻同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。
  当然这种可见性不是原子性哦。当遇到以下情况会有问题
  1.多个线程同时修改变量且修改时依赖变量本身。
  2.多个volatile变量维护一个条件,若是别的线程对其多个变量修改,那么可能造成条件的不成立。
  针对上述情况于是有了原子变量(后面介绍),保证了其原子性。
  volatile还有一个特性就是禁止指令重排序。那么什么是指令重排序呢?
  指令重排序:编译器的字节码的重排序。cpu指令的重排序。
  指令重排序的目的是在不改变单线程下程序的逻辑下,优化程序执行效率。对于多线程于是就有了问题。有的程序时单线程下,调换一下顺序也没什么的,但是,对于多线程,调换一下顺序,可能回到其他线程造成大的影响。
  而volatile则是解决了这个问题,他利用了内存屏障来来辅助解决了这个。

原子变量

  说到原子变量就不得不说CAS。
  什么是CAS呢?
  就是更新一个值的时候,查询内存中的值,和自己要更新前获取到的值是否一致,若是一致,那么更新。
  与synchronized相比,cas是乐观锁,我认为并发不会修改到我的值,不加锁,只是提前获取到值,要更新的时候在比对一下,若是内存的值和我的值一致,那么更新,否则不更新。而synchronized则是不管什么直接加锁的。因此是悲观锁。
  什么是ABA问题?
  3个线程A,B,C对cas变量a修改。A,B线程获取到了变量a,A修改变量为b,B线程阻塞,C线程获取到变量b,并把b改成了a,B线程不阻塞了,继续执行,执行成功,这就是ABA问题。这个B线程不应该执行的,但是还是执行了。如这个变量是个对象,其引用没有变化,但是具体指变了,那么会出大问题的。解决方案就是在cas变量上加个版本号或者时间戳来限定。
  具体demo就是Atomic开头的类。具体我就不详细说了。
  与加锁相比这个更加轻量级。

ThreadLocal

  线程本地变量是说,每个线程都有同一个变量的独有拷贝ThreadLocal是一个泛型类,接受一个类型参数T,它只有一个空的构造方法。这个直接看个demo


package thread;

public class ThreadLocal001 {
static ThreadLocal

public static void main(String[] args) throws InterruptedException {
    Thread child = new Thread() {
        @Override
        public void run() {
            System.out.println("child" + local.get());
            local.set(200);
            System.out.println("child" + local.get());
        }
    };
    local.set(100);
    child.start();
    child.join();
    System.out.println("main" + local.get());
}

}


  结果如下:

childnull
child200
main100

  这说明,main线程对local变量的设置对child线程不起作用,child线程对local变量的改变也不会影响main线程,它们访问的虽然是同一个变量local,但每个线程都有自己的独立的值,这就是线程本地变量的含义。

猜你喜欢

转载自www.cnblogs.com/donghang/p/9233819.html