导读:题目中提到的几个关键字,分别是解决并发问题中,加锁所使用到的几个关键字,每个关键字代表的锁的粒度 不同,本篇博客,主要是从概念定义上,区分这几个关键字的应用场景。(PS:睡梦中,依稀记得有回面试的时候,问了我一个问题:你们在加锁的时候,加多大的锁? 哇塞,当时愣了一下,压根儿就没有这个大小的概念,我真的以为都是一样的)

话说,就像加锁日记本的锁是个很小的艺术锁,保险箱一般是密码锁(或者什么指纹人脸瞳孔识别之类的),锁铁门的一般都是那种大号锁,所以,仅从生活考虑,这个锁也是分大小的,唉,为毛自己写代码的时候,却没有注意过,不过,应该说,我至今为止,只用过final和Synchronized !好了,看下面的解析吧,话说多了都是泪!

首先,锁的粒度支持,也就是对Load、Store的各种顺序控制,load、store两两组合为4种情况:LoadLoad、StoreStore、LoadStore、StoreLoad,他们以一种指令屏障的方式来控制顺序。绝大部分系统,都支持StoreLoad。

备注:JVM中一些普通变量的操作指令

1,Load操作(将本地变量推至栈顶,用来给CPU调度运算)发生在read之后(之间可以有其他的指令)

2,普通变量的修改未必会理解发生Store操作(将栈顶的数据写入本地变量),但发生Store操作,就会发生write操作

一、ThreadLocal

ThreadLocal可以放一个线程级别的变量,但是它本身可以被多个线程共享使用,而且又可以达到线程安全的目的,且绝对线程安全

理解:这就是一个线程安全的全局变量,呵呵,全局变量,就是动一个地方,到处都变了的那位,再代码设计比较乱的情况下,如果用了很多ThreadLocal,那这个系统就会慢慢的神龙见首不见尾,要是再整出一个bug,就真的呵呵了。 要点:ThreadLocal是一种对象持有的方式,每个线程都有一个ThreadLocalMap,而这个ThradLocal则相当于是一个Key值,要保持的对象作为value值。

ThreadLocal的“坑”:

1,ThreadLocal是一个与线程绑定的变量,所以说,如果没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将与线程共存,第一个坑:不知道它的作用域范围

2,理论上说,线程结束后ThreadLcoal就会被回收,但事实可能并非如此。比如说,在线程池中对线程管理都是采用线程复用的方法,在线程池中线程很难结束甚至永远不会结束,第二个坑:ThreadLocal变量的生命周期不可预测

3,理论上每次set数据时,使用ThreadLocal本身作为Key,相同的Key肯定会替换原来的数据,原来的数据就会被释放,但是,如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次从同一个ThreadLocal中取出对象,再对内容进行操作,第三个坑:内部的集合类和复杂对象所占用的空间膨胀

填坑tips:让ThreadLocal的入口和出口可控,用finally去remove数据,为了不破坏ThradLocal的入口,一般在使用之前,会调用get()方法判断是否为null

为什么说ThradLocal是与线程绑定的(java7源码),在jThreadLocal类中,主要包含三个方法:set(),get(),remove()

    /**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
} /**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* <tt>initialValue</tt> method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

备注:在任何异步程序中(包括异步I / O,非阻塞I / O),ThreadLocal的参数传递都是不靠谱的,因为线程将请求发送后,就不再等待远程返回结果继续向下执行了,真正的返回结果得到后,处理的线程可能是另一个。

二、volatile

volatile被称为是“最轻量级的锁”,因为它只是在读这个瞬间要求一个简单的顺序,而不是一个变量上的原子读写,或者在一段代码上的同步!

volatile要求在对变量进行读\ 写操作时,其前后的指令在某些情况下不允许进行重排序,这种限制主要体现在以下3中情况:

1,如果是一条对volatile变量进行赋值操作的代码,那么在该代码前面的任何代码不能与这个赋值操作交换顺序;如果是一条读取volatile变量的代码,则正好相反

2,普通变量的读写操作相互之间是可以重排序的,只要不影响它们之间的逻辑语义顺序就可以重排序,但是如果普通变量的读\ 写操作遇上了volatile变量的操作,就需要遵循前一个基本原则

3,如果两个volatile变量的读/ 写操作都在一段代码中,则依然遵循前两个基本原则,此时不管两者之间的读\ 写顺序如何,都肯定不会重排序

理解:其实说到这个volatile变量,就想到那个sql,就是说发出sql查询语句,只会获取到发出sql语句时的数据块数据,而不会获取到之后的数据。比如说,我在9点1分1秒发出了一条查询语句,然后在9点1分2秒执行了update操作,那么我拿到的数据,只会是update之前的数据。这个volatile变量,感觉就是一样的,如果我有一个线程正在读取这个变量,那么另一个写操作的线程,就必须等待我这个读操作结束,因为这个volatile不允许重排序!

三、synchronized

Synchronized是一把锁,始终保证临界区的访问控制。临界区:指访问这个地方最多只能有一个线程在里面!

对于这一个,结合到自己在项目中应用,我只想问我自己一个问题:姑娘,你难道只会把Synchronized关键字加到方法块吗?我要反思的是,为什么我加锁的最小粒度是一个完整的方法?其实,很多时候,这个锁的范围真的变大了,频繁的锁征用,就进入了悲观锁的状态!在加锁的时候,应该注意一下锁的粒度问题,节省不必要的开销!

四、Atomic

PS:想想事务的原子性,再想想乐观锁的实现原理,这个东西秒懂

Atomic为我们提供了一些列java原子变量的操作方法,其中,Atomic提供的原子操作类有(java 7源码):

可分为4中类型:

1,基本变量操作:Boolean,Integer,Long

2,引用(reference)操作:也就是对象引用的操作,可以做到原子级别,当多个线程对同一个引用发生修改时,只会有一个成功(存在对ABA问题的处理)

3,数组(Array)操作:这个操作并不是操作数组的对象,而是数组中的每一个元素,针对每一个元素的读写操作是线程安全的

4,Updater:java提供了一种updater机制,可以在原有的类定义volatile变量的基础上,实现一种原子性的管理,而不需要将变量本身定义为Atomic,这样可以在不破坏原有程序逻辑的基础上实现一致性的读写

简单说来,它的原子性实现,是基于可见性,修改前提取、修改后对比来确定是否写回到主存。请看下面的关键代码,以AtomicInteger为例(其他的几个操作类都类似,java7源码):

/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
} /**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

从代码中对compareAndSet方法的调用,可以知道,它每次修改,都会有一个期待值和当前值的对比,如果一致,则写入!

实现原子性,主要就是两种方法,一种是总线加锁,也就是我们常说的悲观锁;另外一种就是缓存加锁,对应着乐观锁

五、总结

关于这几个关键字的介绍就到这里了,简单说来,volatile和ThreadLocal主要是加在变量上,而Synchronized和Atomic是代码块或者更大级别的锁(Atomic可以不破坏原有程序的逻辑)

以后自己再处理并发的时候,对于锁的概念,估计能清楚点。写这篇博客,看了书、参考了博客、还看了源码,我也真是够了!我足足写了差不多1天,就废在这几个关键字上,我勒个去。然后还有关于信号量的问题还没有解决,好多好多。。。。。。

【java基础 14】锁的粒度:ThreadLocal、volatile、Atomic和Synchronized的更多相关文章

  1. 2015年11月26日 Java基础系列(三)ThreadLocal类初级学习

    序,ThreadLocal类是为了解决多线程的安全问题.线程安全的意思也就是说每个线程操作自己的变量,不要对其他线程的值造成影响. 在很多情况下,ThreadLocal比直接使用synchronize ...

  2. 面试【JAVA基础】锁

    1.锁状态 锁的状态只能升级不能降级. 无锁 没有锁对资源进行锁定,所有线程都能访问并修改同一个资源,但同时只有一个线程能修改成功.其他修改失败的线程会不断重试,直到修改成功,如CAS原理和应用是无锁 ...

  3. 深入浅出 Java Concurrency (14): 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)

      这一节主要是谈谈读写锁的实现. 上一节中提到,ReadWriteLock看起来有两个锁:readLock/writeLock.如果真的是两个锁的话,它们之间又是如何相互影响的呢? 事实上在Reen ...

  4. 深入浅出 Java Concurrency (14): 锁机制 part 9 读写锁 (ReentrantReadWriteLock) (2)[转]

    这一节主要是谈谈读写锁的实现. 上一节中提到,ReadWriteLock看起来有两个锁:readLock/writeLock.如果真的是两个锁的话,它们之间又是如何相互影响的呢? 事实上在Reentr ...

  5. 重学JAVA基础(八):锁的基本知识

    1.线程状态 如上图,当我们新建一个线程,并start后,其实不一定会马上执行,因为只有操作系统调度了我们的线程,才能真正进行执行,而操作系统也随时可以运行其他线程,这时线程又回到可运行状态.这个过程 ...

  6. Java基础9-死锁;String;编码

    昨日内容回顾 死锁案例 class DeadLock{ public static void main(String[] args){ Pool pool = new Pool(); Producer ...

  7. java基础14 多态(及关键字:instanceof)

    面向对象的三大特征: 1.封装   (将一类属性封装起来,并提供set()和get()方法给其他对象设置和获取值.或者是将一个运算方法封装起来,其他对象需要此种做运算时,给此对象调用) 2.继承   ...

  8. Java基础14:离开IDE,使用java和javac构建项目

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  9. java基础(14):Eclipse、面向对象、自定义数据类型的使用

    1. Eclipse的应用 1. 常用快捷操作 Ctrl+T:查看所选中类的继承树 例如,在下面代码中,选中Teacher类名,然后按Ctrl+T,就会显示出Teacher类的继承关系 //员工 ab ...

随机推荐

  1. LR脚本录制方式说明

    1.LR脚本录制方式说明1)HTML-based script基于HTML的脚本从内存中读取并下载资源,较少的关联处理,可以加入图片检查,回放时需要解析返回的信息a-基于用户行为的方式 web_lin ...

  2. Python各种参数类型

    1. Python的参数传递是值传递还是引 举例说明Python函数参数传递的几种形式,并说明函数传参是值传递还是引用传递 一.位置参数 调用函数时根据函数定义的参数位置来传递参数.例子: def p ...

  3. Linux之bash shell的学习

    1.什么是bash  shell bash 是Bourne Again Shell的简称,是从unix系统中的sh发展而来,是用户和偶Linux内核交互的工具,用户通过bash操作内核完成系统的使用和 ...

  4. vue.js的package.json相关问题解惑

    使用vue-cli创建vue.webpack项目,在项目中用到了iSlider移动端滑动插件,只在本地命令工具中npm install islider.js:提交之后,partner下载代码后一直运行 ...

  5. LibreOJ #2037. 「SHOI2015」脑洞治疗仪

    线段树区间合并问题 恶心... 屠龙宝刀点击就送 #include <cstdio> #define N 200005 struct Segment { int l,r,mid,sum,l ...

  6. 在2017年,如何将你的小米4刷上Windows 10 mobile?(后附大量图赏)

    众多攻略集大成者!资深软粉亲测有效! 参考教程: http://bbs.xiaomi.cn/t-11814358 http://bbs.xiaomi.cn/t-11736827 问:刷机前,我需要做什 ...

  7. Unity基础-脚本的加载与编译顺序

    脚本的加载与编译顺序 C#是以Assembly(汇编集)为一个基本单元组织代码的,dll就是一个assembly,dll之间有加载以来顺序 Assets/*.dll Stamdard Assets/* ...

  8. charles 模拟手机弱网、修改请求参数、修改返回值

    1.charles模拟弱网(断网) 2.charles修改请求参数 (1)先访问一次需要改的请求,在charles上找到相应的请求地址 (2)然后在需要打断点的请求上右键,勾选[Breakpoints ...

  9. django开发之model篇-Field类型讲解

    今天介绍一下django开发中,定义模型时用到的相关字段类型和字段选项. 先说说常用的字段类型:1) AutoField: 自增字段类型,当自定义自增类型的id时,可以使用此类型:2) BigAuto ...

  10. css3属性:美化表单、点击元素产生的背景与边框怎么去掉,滚动回弹效果