一、请谈谈volatile有什么特点,为什么它能保证变量对所有线程的可见性?
当一个变量被volatile修饰后,具备两个特性:
- 保证此变量对所有线程的可见性,当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的,而普通变量做不到这一点。
- 禁止指令重排序优化,普通变量仅仅能保证在方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序。
内存模型定义了8种内存间操作来保证可见性:
- lock/unlock
- read/write
- load/store
- use/assgin
二、volatile能保证线程间的变量可见性,是不是就意味着基于volatile变量的运算就是并发安全的?
这很显然并不是的。基于volatile变量的运算在并发下不一定是线程安全的,volatile变量 在各个线程的工作内存,不存在一致性问题(各个线程的工作内存中volatile变量,每次使用前都要刷新到主内存),但是java里面的运算并不是原子操作,导致volatile变量的运算在并发下也不一定就是线程安全的。volatile只保证了可见性,并不能保证原子性。
三、请对比下volatile和Synchronized的异同?
- Synchronized既保证了可见性,又保证了原子性,而volatile只能保证可见性,不能保证原子性;
- volatile仅能使用在变量级别;Synchronized则可以使用在变量、方法、和类级别的;
- volatile本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;Synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住;
- volatile不会造成线程的阻塞;Synchronized可能会造成线程的阻塞;
- volatile标记的变量不会被编译器优化;Synchronized标记的变量可以被编译器优化;
四、请谈谈ThreadLocal是怎么解决并发安全的?
ThreadLocal是Java提供的一种保存线程私有信息的机制,因为其在整个线程生命周期内有效,所以可以方便地在一个线程关联的不同业务模块之间传递信息,比如事务ID、Cookie等上下文信息。 ThreadLocal为每一个线程维护变量的副本,把共享数据的可见范围限制到同一个线程之内,其实现原理是,在 ThreadLocal类中有一个Map,用于存储每个线程的变量副本。
五、谈谈你对ThreadLocal的理解,使用ThreadLocal需要注意些什么?
- ThreadLocal 变量解决了多线程环境下单个线程中变量的共享问题,使用名为ThreadLocalMap的哈希表进行维护(key为ThreadLocal变量名,value为ThreadLocal变量的值);
- 设想如果将一个大对象放入ThreadLocal 中,并且还没有remove。那么就可能会造成OutOfMemoryError,如果不会造成OutOfMemoryError那么也会浪费Java堆内存。使用时需要注意以下几点:
- 线程之间的threadLocal变量是互不影响的;
- 使用private final static进行修饰,防止多实例时内存的泄露问题;
- 线程池环境下使用后将threadLocal变量remove掉或设置成一个初始值;