Synchronized、Threadlocal、Volatile

synchronized:
synchronized叫做同步锁,操作起来方便,只需要在一个方法或把需要同步的代码块包装在它内部,那么这段代码就是同步的了,所有线程对这块区域的代码访问必须先持有锁才能进入,否则则拦截在外面等待正在持有锁的线程处理完毕再获取锁进入正因为它基于这种阻塞的策略,所以它的性能不太好,但是由于操作上的优势,
只需要简单的声明一下即可,而且被它声明的代码块也是具有操作的原子性。

threadlocal(本地单机)
用来提供线程内的局部变量,这样每个线程都自己管理自己的局部变量,别的线程操作的数据不会对我产生影响,互不影响
就是把变量分成很多个拷贝,每个线程拥有一个。这里没有所谓的最后的结果,每个线程单独操作自己的变量,和其他的变量没关系,互不干扰

ThreadLocalMap类的定义是在ThreadLocal类中,真正的引用却是在Thread类中。同时,ThreadLocalMap中用于存储数据的entry定义,它是一个Map,他的key是ThreadLocal实例对象。

1、JVM利用设置ThreadLocalMap的Key为弱引用,来避免内存泄露。
2、JVM利用调用remove、get、set方法的时候,回收弱引用
3、当ThreadLocal存储很多Key为null的Entry的时候,而不再去调用remove、get、set方法,那么将导致内存泄漏
4、当使用static ThreadLocal的时候,延长ThreadLocal的生命周期,那也可能导致内存泄漏。

在使用完ThreadLocal里的对象后最好能手动remove一下,或者至少调用下ThreadLocal.set(null)
而关于ThreadLocalMap的回收,会在当前Thread销毁之后进行回收

Volatile
Volatile可以看做是一个轻量级的synchronized,它可以在多线程并发的情况下保证变量的“可见性”,什么是可见性?就是在一个线程的工作内存中修改了该变量的值,该变量的值立即能回显到主内存中(java内存分为工作内存和主存),从而保证所有的线程看到这个变量的值是一致的。所以在处理同步问题上它大显作用,而且它的开销比synchronized 小、使用成本更低。

Volatile保证两件事:
1、 线程1工作内存中的变量更新会强制立即写入到主内存;
2、 线程2工作内存中的变量会强制立即失效,这使得线程2必须去主内存中获取最新的变量值。
所以这就理解了Volatile保证了变量的可见性,因为线程1对变量的修改能第一时间让线程2可见

当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者 对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制
volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

java内存分为工作内存和主存
工作内存:即java线程的本地内存,是单独给某个线程分配的,存储局部变量等,同时也会复制主存的共享变量作为本地
                 的副本,目的是为了减少和主存通信的频率,提高效率。
主存:     存储类成员变量等
可见性:    是指的是线程访问变量是否是最新值。(线程之间的可见性,一个线程修改的状态对另一个线程是可见的)
局部变量不存在可见性问题,而共享内存就会有可见性问题,
因为本地线程在创建的时候,会从主存中读取一个共享变量的副本,且修改也是修改副本,
且并不是立即刷新到主存中去,那么其他线程并不会马上共享变量的修改。
因此,线程B修改共享变量后,线程A并不会马上知晓,就会出现上述死循环的问题。
解决共享变量可见性问题,需要用volatile关键字修饰。

可见性的特性总结为以下2点:
1. 对volatile变量的写会立即刷新到主存
2. 对volatile变量的读会读主存中的新值

:volatile关键字是否能保证线程安全?      

          volatile关键字用在多线程同步中,可保证读取的可见性,JVM只是保证从主内存加载到线程工作内存的值是最新的读取值,而非cache中。但多个线程对volatile的写操作,无法保证线程安全。例如假如线程1,线程2在进行read,load操作中,发现主内存中count的值都是5,那么都会加载这个最新的值,在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6;线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6;导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

总结
1、对于synchronized的出现,是解决多线程资源共享的问题,同步机制采用了“以时间换空间”的方式:访问串行化,对象共享化。同步机制是提供一份变量,让所有线程都可以
访问。

2、对于ThreadLocal的出现,并不是解决多线程资源共享的问题,而是用来提供线程内的局部变量,省去参数传递这个不必要的麻烦,ThreadLocal采用了“以空间换时间”的方式
:访问并行化,对象独享化。ThreadLocal是为每一个线程都提供了一份独有的变量,各个线程互不影响。

3、对于Volatile,为多线程资源共享问题解决了部分需求,在非依赖自身的操作的情况下,对变量的改变将对任何线程可见。

执行第二个指令 必须要在第一个指令执行结束
不让它重排,在两条指令中间加一道屏障。即 屏障两侧的写指令不能重排(Store Load屏障)

4、Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个
变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。(有序性)

群交流(262200309)
原文地址:https://www.cnblogs.com/webster1/p/8465459.html