系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized、ReentrantLock、volatile。

一、synchronized

  互斥锁,即操作互斥,并发线程过来,串行获得锁,串行执行代码。就像一个房间一把钥匙,一个人进去后,下一个人得等第一个人出来得到钥匙才能进入。如果代码写的不好(A),可能出现死锁!(A得到锁,B等待A释放锁,A不释放,B死锁)

示例代码:

   //修饰静态方法:类级别互斥(只要是房子此方法就互斥)
public synchronized static void foo1(){
//do
} //修饰普通方法:对象级别互斥(只有同一间房子此方法互斥)
public synchronized void foo2(){
//do
} //修饰代码块
public void foo3(){
//类级别互斥
synchronized(DennyTest.class) {
//do
}
} //修饰代码块
public void foo4(){
//对象级别互斥
synchronized(this) {
//do
}
}

需要注意的是,经常使用的是对象级别的互斥,那么特别需要注意是同一个对象的锁。新手经常犯错,都不是同一个对象,当然锁不住。

二、ReentrantLock

可重入锁,和同步锁功能类似,不过需要显示的创建、销毁。特点:

1.ReentrantLock有tryLock方法,如果锁被其他线程持有,返回false,可避免形成死锁。

2.创建时可自定义是否可抢占。

3.ReentrantReadWriteLock,用于读多写少,且读不需要互斥的场景,大大提高性能。

示例代码:

1、尝试获取一次
ReentrantLock lock = new ReentrantLock();
if (lock.tryLock()) { //得到执行,得不到不执行,就一次。
try {
//操作
} finally {
lock.unlock();
}
}
2、同步执行,类似synchronized(也是使用最多的)
ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁:可抢占
ReentrantLock lock = new ReentrantLock(true); //公平锁:严格按照请求锁的排队顺序获取锁 lock.lock(); //如果被其它资源锁定,会在此等待锁释放,阻塞
try {
//操作
} finally {
lock.unlock();
}
3、尝试等待固定时间再次获取
ReentrantLock lock = new ReentrantLock(true); //公平锁
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
//如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false
try {
//操作
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace(); //当前线程被中断时(interrupt),会抛InterruptedException
}
4、可中断锁的同步执行
ReentrantLock lock = new ReentrantLock(true); //公平锁
lock.lockInterruptibly();
try {
//操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}

三、volatile

volatile,翻译过来是易变的。只保证同一变量在多线程中的可见性。

  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

  2)它会强制将对缓存的修改操作立即写入主存;

  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

注意:虽然保证变量是主存数据,但是操作不是原子的,多线程读取到同一个值(是主存的值),同时进行判断或者操作,导致出错。

总结:

可见,synchronized和ReentrantLock是一个级别的,但是volatile只是一个轻量级的关键字。可用场景:

1.状态标记

 volatile boolean inited = false;
//线程1:
context = loadContext();
inited = true; //线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);

2.double check :使用 volatile 关键字来保证多线程下的单例

    public class Singleton {
private volatile Singleton instance = null;
public Singleton getInstance() {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

参考资料:

《深入理解Java虚拟机》

《effective java》

http://www.cnblogs.com/dolphin0520/p/3920373.html

高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile的更多相关文章

  1. 高级java必会系列一:多线程的简单使用

    众所周知,开启线程2种方法:第一是实现Runable接口,第二继承Thread类.(当然内部类也算...)常用的,这里就不再赘述.本章主要分析总结线程池和常用调度类. 一.线程池 1.newCache ...

  2. 高级java必会系列一:常用线程池和调度类

    众所周知,开启线程2种方法:第一是实现Runable接口,第二继承Thread类.(当然内部类也算...)常用的,这里就不再赘述. 一.线程池 1.newCachedThreadPool (1)缓存型 ...

  3. 高级java必会系列一:zookeeper分布式锁

    方案1: 算法思路:利用名称唯一性,加锁操作时,只需要所有客户端一起创建/test/Lock节点,只有一个创建成功,成功者获得锁.解锁时,只需删除/test/Lock节点,其余客户端再次进入竞争创建节 ...

  4. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  5. 【JAVA编码】 JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换

    http://blog.csdn.net/qinysong/article/details/1179489 这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记 ...

  6. java基础解析系列(二)---Integer

    java基础解析系列(二)---Integer 前言:本系列的主题是平时容易疏忽的知识点,只有基础扎实,在编码的时候才能更注重规范和性能,在出现bug的时候,才能处理更加从容. 目录 java基础解析 ...

  7. Java多线程之内存可见性和原子性:Synchronized和Volatile的比较

    Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article ...

  8. 高级Java必看的10本书

    1.深入理解Java虚拟机:JVM高级特性与最佳实践 本书共分为五大部分,围绕内存管理.执行子系统.程序编译与优化.高效并发等核心主题对JVM进行了全面而深入的分析,深刻揭示了JVM的工作原理. 2. ...

  9. 系统学习java高并发系列二

    转载请注明原创出处,谢谢! 什么是线程? 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程 ...

随机推荐

  1. [转] CC 命令

    1.最基本的操作cleartool co -nc xxx.cppcleartool ci -nc xxx.cpp 2.查看自己总共co了多少文件cleartool lscheckout -cview ...

  2. Swift 提示:Initialization of variable was never used consider replacing with assignment to _ or removing it

    Swift 提示:Initialization of variable was never used consider replacing with assignment to _ or removi ...

  3. Arch Linux 安装博通 BCM4360 驱动(Arch Linux, Ubuntu, Debian, Fedora...)

    BCM4360 在2010年9月,博通完全开源的硬件驱动[1].该驱动程序 brcm80211已被列入到自2.6.37之后的内核中.随着2.6.39发布,这些驱动程序已被重新命名为 brcmsmac和 ...

  4. 51nod1228 序列求和(自然数幂和)

    与UVA766 Sum of powers类似,见http://www.cnblogs.com/IMGavin/p/5948824.html 由于结果对MOD取模,使用逆元 #include<c ...

  5. js冒泡排序

    今天面试了家公司,最后要写个js的简单数组排序,很久都写不出来,好尴尬,随着语言的发展,这些简单方法越来越不被重视了... <html> <head> <script t ...

  6. 前端SEO技巧

    前几天在慕课网上学习了“SEO在网页制作中的应用”,觉得挺好.挺有用的,今天,特此做了一个小小的笔记,也算是对学习过后的一个总结. 一.搜索引擎工作原理 当我们在输入框中输入关键词,点击搜索或查询时, ...

  7. C# 退出程序

    1.this.Close();   只是关闭当前窗口,若不是主窗体的话,是无法退出程序的,另外若有托管线程(非主线程),也无法干净地退出: 2.Application.Exit();  强制所有消息中 ...

  8. Node.js-Socket.IO【1】-身份验证

    Websocket身份验证失败的时候,希望向前台传输错误信息,但是Socket.IO目前最新版本1.4.6在后台使用 next(new Error('unauthorization')); 前端的代码 ...

  9. 今天必须完成ireport+jasperreport转成pdf

    中午之前解决字体问题 2.问题总结 (1)Caused by: java.lang.NoSuchMethodException: Unknown property 'objectModelBean98 ...

  10. MongoDB-C#驱动基本操作

    #region IMongoQuery //Query.EQ("", val);//字段值=val //Query.NE("", val);//字段值!=val ...