synchronized关键字,Lock接口以及可重入锁ReentrantLock
多线程环境下,必须考虑线程同步的问题,这是因为多个线程同时访问变量或者资源时会有线程争用,比如A线程读取了一个变量,B线程也读取了这个变量,然后他们同时对这个变量做了修改,写回到内存中,由于是同时做修改,就会导致修改的状态不一致.
用一个实际的例子来说明线程同步的必要性:
package cn.outofmemory.locks;
public class LockDemo implements Runnable {
private int counter = 0;
public void run() {
int loopTimes = 10000;
while (loopTimes > 0) {
counter ++;
loopTimes --;
}
}
public static void main(String[] args) throws InterruptedException {
LockDemo demo = new LockDemo();
Thread[] threads = new Thread[]{
new Thread(demo), new Thread(demo),new Thread(demo), new Thread(demo),new Thread(demo)
};
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
System.out.println("demo's counter is " + demo.counter);
}
}
这段代码中的LockDemo类实现了Runnable接口,在run方法中对其私有变量counter递加了10000次。在main方法中我们首先初始化了一个LockDemo对象,然后初始化了5个线程,这5个线程公用一个LockDemo的实例。
然后我们一次启动这5个线程,然后通过join等待所有线程结束,最后输出demo实例的counter值来。
运行程序,我这儿得到这样一个输出结果:
demo's counter is 44041
本来5个线程每个线程递加10000次,应该得到的结果是50000,而实际的结果是44041.
如果你也运行此程序有可能会得到不一样的结果。这取决于这5个线程造成了多少次的冲突。从我的输出结果看,这段程序的5个线程造成了大约6000次的内存争用冲突。
在实际应用中,这是不可用的。
改进程序,避免冲突
我们可以分析一下,这5个线程的冲突出现在什么地方,他们公用了demo对象,同时对demo对象的成员变量counter做递加,也就是说冲突出现在对counter递加这一步上。
我们在这一步操作上加上synchronzied关键字,让5个线程执行到对counter++这步代码时单独运行,应该就可以解决问题了。
修改后的run方法代码:
public void run() {
int loopTimes = 10000;
while (loopTimes > 0) {
synchronized (this) {
counter ++;
}
loopTimes --;
}
}
我们再次运行程序会得到如下确定的输出结果:
demo's counter is 50000
这次得到的结果是符合我们的预期的,我们通过synchronized关键字解决了问题。
synchronized关键字是jvm虚拟机的关键字,在java.util.concurrent.locks命名空间中还有一个Lock接口,和Lock接口的实现类ReentrantLock(可重入锁)。 ReentrantLock可以实现和synchronized关键字相同的功能,而且更为灵活,在极端的情况下性能会更好一些。
我们看下使用可重入锁ReentrantLock解决线程同步的方法:
private final Lock lock = new ReentrantLock();
public void run() {
int loopTimes = 10000;
while (loopTimes > 0) {
try {
lock.lock();
counter ++;
} finally {
lock.unlock();
}
loopTimes --;
}
}
我们在LockDemo中添加了一个final的成员变量lock,它是一个ReentrantLock的实例。 在run方法中,在counter++这行代码两边加上了try .. finally ..语句,
当线程执行到try块之后,首先通过lock.lock()获得锁,获得锁之后再执行counter++,最后在finally语句块中通过lock的unlock方法释放锁。
我们可以运行修改后的代码,输出如下:
demo's counter is 50000
输出结果符合逻辑预期。
synchronized获得的内部锁存在一定的局限
1. 不能中断一个正在试图获得锁的线程
2. 试图获得锁时不能像trylock那样设定超时时间
3. 每个锁只有单一的条件,不像condition那样可以设置多个
synchronzied关键字和可重入锁ReentrantLock选择的最佳实践:
1. 如果synchronized关键字适合程序,尽量使用它,可以减少代码出错的几率和代码数量
2. 如果特别需要Lock/Condition结构提供的独有特性时,才使用他们
3. 许多情况下可以使用java.util.concurrent包中的一种机制,它会为你处理所有的加锁情况
- 推荐阅读:
-
- java并发控制:ReentrantLock Condition使用详解
- java线程组(ThreadGroup)
- Java 抽象类
- [韩顺平]Java从入门到精通第8讲-this.类变量
- 韩顺平.循序渐进学.java.从入门到精通.第24讲-集合补充.avi
- 韩顺平.循序渐进学.java.从入门到精通.第40讲-线程.avi
- java语言从入门到精通——运算符与表达式 07
- java语言从入门到精通——抽象类和抽象函数 23
- java语言从入门到精通——Java当中的数组 40
- JAVA从入门到精通(MLDN) 1.4 安装JAVA开发工具箱——JDK 1.6.0_17多国语
- 服务幂等以及常用实现方式
synchronized关键字,Lock接口以及可重入锁ReentrantLock的更多相关文章
- Java 重入锁 ReentrantLock 原理分析
1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...
- 17_重入锁ReentrantLock
[概述] 重入锁可以完全代替synchronized关键字. 与synchronized相比,重入锁ReentrantLock有着显示的操作过程,即开发人员必须手动指定何时加锁,何时释放锁,所以重入锁 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理
转载自https://blog.csdn.net/yanyan19880509/article/details/52345422,(做了一些补充) 前言 相信学过java的人都知道 synchroni ...
- java 可重入锁ReentrantLock的介绍
一个小例子帮助理解(我们常用的synchronized也是可重入锁) 话说从前有一个村子,在这个村子中有一口水井,家家户户都需要到这口井里打水喝.由于井水有限,大家只能依次打水.为了实现家家有水喝,户 ...
- 轻松学习java可重入锁(ReentrantLock)的实现原理(转 图解)
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
- Java 显示锁 之 重入锁 ReentrantLock(七)
ReentrantLock 重入锁简介 重入锁 ReentrantLock,顾名思义,就是支持同一个线程对资源的重复加锁.另外,该锁还支持获取锁时的公平与非公平性的选择. 重入锁 ReentrantL ...
- Java中可重入锁ReentrantLock原理剖析
本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一. 概述 本文首先介绍Lock接口.ReentrantLock的类层次结构以及锁功能模板类AbstractQue ...
- 多线程通信的两种方式? (可重入锁ReentrantLock和Object)
(一)Java中线程协作的最常见的两种方式: (1)利用Object的wait().notify()和notifyAll()方法及synchronized (2)使用Condition.Reentra ...
- Java多线程——深入重入锁ReentrantLock
简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为“独占锁”. ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychronized,是实现 ...
随机推荐
- SQL Server 2012 列存储索引分析(翻译)
一.概述 列存储索引是SQL Server 2012中为提高数据查询的性能而引入的一个新特性,顾名思义,数据以列的方式存储在页中,不同于聚集索引.非聚集索引及堆表等以行为单位的方式存储.因为它并不要求 ...
- [51单片机] SPI nRF24L01无线 [可以放在2个单片机里实现通信]
main.c #include<reg51.h> #include"2401.h" #define uint unsigned int #define uchar un ...
- 整合GreyBox放大显示图片
<s:iterator value="#request.photoList" id="photo" status="stu"> ...
- paip.语义相关是否可在 哈米 的语义分析中应用
paip.语义相关是否可在 哈米 的语义分析中应用 作者Attilax 艾龙, EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn. ...
- 测量行业(RTK)相关的小知识总结
RTK测量系统,一般需要先架设基准站(参考站),数据链模式外挂大电台(有天线大功率模式,理想范围方圆18公里以内).基准站通过收到卫星(多星)进行计算出当前的位置. 其中涉及到频率的设置,波特率,以及 ...
- cmd命令生成android签名证书
cmd命令生成android签名证书,有空在写一篇eclipse导出带签名的apk,这里面包括生成新的签名.现在还是讲讲在cmd怎么操作生成签名证书. 1.dos下进入JDK的bin目录 运行如下命令 ...
- eclipse 打开是报错"reload maven project has encountered a problem"
不需要删除整个 .metadata 如果删除这个代价是重新导入全部项目 D:\eclipse-workspace\.metadata\.plugins\org.eclipse.e4.workbench ...
- Leetcode 168 Excel Sheet Column Title 进制数转化
题意:将数字转化成excel表中的行中的项目 本质是10进制转化为26进制,但是在中间加入了一个不一样的操作,在每次操作前都需要n-- class Solution { public: string ...
- 多层嵌套对象无法使用gson反序列化的问题The JsonDeserializer com.google.gson.DefaultTypeAdapters$CollectionTypeAdapter@3bf13cde failed to de
http://www.oschina.net/question/1256646_123691 http://bbs.csdn.net/topics/360005140 http://stackover ...
- POI 设置EXCEL单元格格式(日期数字文本等)
HSSFCellStyle style0 = workbook2003.createCellStyle(); style0.setBorderBottom(HSSFCellStyle.BORDER_T ...