- 浏览: 395527 次
- 性别:
- 来自: 杭州
文章分类
- 全部博客 (760)
- 股票日志 (26)
- Selenium (0)
- selenium 2 环境的搭建 (1)
- 并发 (7)
- 框架开发 (1)
- 动态代理 (2)
- Struts2 (2)
- POI (2)
- jdk (3)
- maven (31)
- spring (35)
- mysql (31)
- 工作机会 (3)
- xtream (1)
- oracle dbms_metadata GET_DDL (0)
- SSI (1)
- DB (61)
- powermock (4)
- java 基础 (25)
- 多线程 (11)
- 高手 (2)
- java 底层 (2)
- 专业网站 (1)
- 开发联想 (1)
- 开发联想 (1)
- bat文件 (2)
- 清queue 语句 (1)
- 清queue 语句 (1)
- jquery (7)
- html5 (1)
- Jenkins (10)
- Linux (17)
- 工作issue (2)
- tomcat log (3)
- jvm (23)
- 项目细节 (0)
- oracle (41)
- 泛型 (3)
- 新知识点 (1)
- 数据库ddl 语句 (0)
- AQ (2)
- jms (0)
- 网络资源 (6)
- github (6)
- Easymock (1)
- Dom 解析XML (1)
- windows命令 (2)
- java (7)
- 正则表达式 (5)
- sequence (1)
- oracle 表meta信息 (1)
- 小工具技巧 (1)
- 辅助工具 (1)
- Junit (1)
- 泛型 generic (2)
- Java程序设计 (1)
- cglib (2)
- 架构师之路 (1)
- 数据库连接池 (5)
- c3p0 (1)
- eclipse使用 (1)
- oracle sql plus (1)
- 码农人生 (3)
- SVN (15)
- sqlplus (2)
- jsoup (1)
- 网络爬虫 (2)
- 新技能 (1)
- zookeeper (4)
- hadoop (1)
- SVNKIT (1)
- 从工具到知识点的整理 (1)
- log4j (13)
- 读文件 (0)
- 转义字符 (1)
- command (1)
- web service (3)
- 锁 (1)
- shell 脚本 (1)
- 遇到的错误 (2)
- tomcat (14)
- 房产 (5)
- bootstrap jquery ui (1)
- easyui (2)
- 个人征信 (1)
- 读写分离 (1)
- 备份 (1)
- rmi (6)
- webservice (1)
- JMX (4)
- 内存管理 (3)
- java设计 (1)
- timer (1)
- lock (2)
- concurrent (2)
- collection (1)
- tns (1)
- java基础 (15)
- File (1)
- 本机资源 (1)
- bat (1)
- windows (4)
- 数据结构 (3)
- 代码安全 (1)
- 作用域 (1)
- 图 (2)
- jvm内存结构 (1)
- 计算机思想 (1)
- quartz (6)
- Mongo DB (2)
- Nosql (4)
- sql (5)
- 第三方Java 工具 jar 项目 (2)
- drools (1)
- java swing (2)
- 调用console (1)
- runtime (1)
- process (1)
- swing (2)
- grouplayout (1)
- dubbo (0)
- bootstrap (0)
- nodejs (2)
- SVN hooks (1)
- jdbc (3)
- jdbc error (1)
- precedure (1)
- partition_key (1)
- active mq (1)
- blob (2)
- Eclipse (6)
- web server (1)
- bootstrapt (2)
- struts (1)
- ajax (1)
- js call back (1)
- 思想境界拓展 (1)
- JIRA (1)
- log (1)
- jaxb (3)
- xml java互相转换 (1)
- 装修 (2)
- 互联网 (2)
- threadlocal (3)
- mybatis (22)
- xstream (1)
- 排序 (1)
- 股票资源 (1)
- RPC (2)
- NIO (3)
- http client (6)
- 他人博客 (1)
- 代理服务器 (1)
- 网络 (2)
- web (1)
- 股票 (5)
- deadlock (1)
- JConsole (2)
- activemq (3)
- oralce (1)
- 游标 (1)
- 12月13日道富内部培训 (0)
- grant (1)
- 速查 (2)
- classloader (4)
- netty (4)
- 设计模式 (2)
- 缓存 (2)
- ehcache (2)
- framework (1)
- 内存分析 (2)
- dump (1)
- memory (2)
- 多高线程,并发 (1)
- hbase (2)
- 分布式系统 (1)
- socket (3)
- socket (1)
- 面试问题 (1)
- jetty (2)
- http (2)
- 源码 (1)
- 日志 (2)
- jni (1)
- 编码约定 (1)
- memorycache (1)
- redis (13)
- 杂谈 (1)
- drool (1)
- blockingqueue (1)
- ScheduledExecutorService (1)
- 网页爬虫 (1)
- httpclient (4)
- httpparser (1)
- map (1)
- 单例 (1)
- synchronized (2)
- thread (1)
- job (1)
- hashcode (1)
- copyonwriteArrayList (2)
- 录制声音 (1)
- java 标准 (2)
- SSL/TLS (1)
- itext (1)
- pdf (1)
- 钻石 (2)
- sonar (1)
- unicode (1)
- 编码 (4)
- html (1)
- SecurityManager (1)
- 坑 (1)
- Restful (2)
- svn hook (1)
- concurrentHashMap (1)
- 垃圾回收 (1)
- vbs (8)
- visual svn (2)
- power shell (1)
- wmi (3)
- mof (2)
- c# (1)
- concurrency (1)
- 劳动法 (1)
- 三国志游戏 (2)
- 三国 (1)
- 洪榕 (2)
- 金融投资知识 (1)
- motan (1)
- tkmybatis mapper (1)
- 工商注册信息查询 (1)
- consul (1)
- 支付业务知识 (2)
- 数据库备份 (1)
- 字段设计 (1)
- 字段 (1)
- dba (1)
- 插件 (2)
- PropEdit插件 (1)
- web工程 (1)
- 银行业知识 (2)
- 国内托管银行 (1)
- 数据库 (1)
- 事务 (2)
- git (18)
- component-scan (1)
- 私人 (0)
- db2 (14)
- alias (1)
- 住房 (1)
- 户口 (1)
- fastjson (1)
- test (6)
- RSA (2)
- 密钥 (1)
- putty (1)
- sftp (1)
- 加密 (1)
- 公钥私钥 (3)
- markdown (1)
- sweet (1)
- sourcetree (1)
- 好工具 (1)
- cmd (1)
- scp (1)
- notepad++ (1)
- ssh免密登录 (1)
- https (1)
- ssl (2)
- js (2)
- h2 (1)
- 内存 (2)
- 浏览器 (1)
- js特效 (1)
- io (1)
- 乱码 (1)
- 小工具 (1)
- 每周技术任务 (1)
- mongodb (7)
- 内存泄漏 (1)
- 码云 (2)
- 如何搭建java 视频服务器 tomcat (1)
- 资源 (1)
- 书 (1)
- 四色建模法 (1)
- 建模 (1)
- 配置 (1)
- 职位 (1)
- nginx (1)
- excel (1)
- log4j2 (2)
- 做菜 (1)
- jmap (1)
- jspwiki (1)
- activiti (1)
- 工作流引擎 (1)
- 安卓 (1)
- acitviti 例子 (1)
- 二维码 (1)
- 工作流 (1)
- powerdesign (2)
- 软件设计 (1)
- 乐观锁 (1)
- 王者荣耀 (1)
- session (2)
- token (5)
- cookie (4)
- springboot (24)
- jwt (2)
- 项目路径 (1)
- magicbook (1)
- requestType (1)
- json (2)
- swagger (1)
- eolinker (1)
- springdata (1)
- springmvc (1)
- controlleradvice (1)
- profile (1)
- 银行四要素 (1)
- 支付人员资源 (1)
- 支付渠道 (1)
- yaml (1)
- 中文编码 (1)
- mongo (2)
- serializable (1)
- 序列化 (1)
- zyd (1)
- unittest (1)
- 工具 (1)
- Something (1)
- 通达信 (1)
- protobuf (1)
- 算法 (1)
- springcloud (2)
- hikari (1)
- rocketmq (7)
- cachecloud (1)
- serfj (1)
- axure (1)
- lombok (1)
- 分布式锁 (1)
- 线程 (2)
- 同步代码块 (1)
- cobar (1)
- mq (1)
- rabbitmq (1)
- 定时执行 (1)
- 支付系统 (3)
- 唱歌 (1)
- elasticjob (1)
- 定时任务 (1)
- 界面 (1)
- flink (2)
- 大数据 (1)
- 接私活 (0)
- 内部培训 (2)
最新评论
-
dannyhz:
做股票从短线 试水,然后 慢慢发现 波段和 中期的故事可挖, ...
搭台唱戏 -
dannyhz:
http://developer.51cto.com/art/ ...
如何自己开发框架 它的注意点是什么
http://blog.csdn.net/natian306/article/details/18504111
在分布式开发中,锁是线程控制的重要途径。Java为此也提供了2种锁机制,synchronized和lock。做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方。
我们先从最简单的入手,逐步分析这2种的区别。
一、synchronized和lock的用法区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
用法区别比较简单,这里不赘述了,如果不懂的可以看看Java基本语法。
二、synchronized和lock性能区别
synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。
说到这里,还是想提一下这2中机制的具体区别。据我所知,synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
现代的CPU提供了指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
我也只是了解到这一步,具体到CPU的算法如果感兴趣的读者还可以在查阅下,如果有更好的解释也可以给我留言,我也学习下。
三、synchronized和lock用途区别
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
下面细细道来……
先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)
这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:
查看源代码打印帮助
1
public class Buffer {
2
3
private Object lock;
4
5
public Buffer() {
6
lock = this;
7
}
8
9
public void write() {
10
synchronized (lock) {
11
long startTime = System.currentTimeMillis();
12
System.out.println("开始往这个buff写入数据…");
13
for (;;)// 模拟要处理很长时间
14
{
15
if (System.currentTimeMillis()
16
- startTime > Integer.MAX_VALUE)
17
break;
18
}
19
System.out.println("终于写完了");
20
}
21
}
22
23
public void read() {
24
synchronized (lock) {
25
System.out.println("从这个buff读数据");
26
}
27
}
28
}
接着,我们来定义2个线程,一个线程去写,一个线程去读。
1
public class Writer extends Thread {
2
3
private Buffer buff;
4
5
public Writer(Buffer buff) {
6
this.buff = buff;
7
}
8
9
@Override
10
public void run() {
11
buff.write();
12
}
13
14
}
15
16
public class Reader extends Thread {
17
18
private Buffer buff;
19
20
public Reader(Buffer buff) {
21
this.buff = buff;
22
}
23
24
@Override
25
public void run() {
26
27
buff.read();//这里估计会一直阻塞
28
29
System.out.println("读结束");
30
31
}
32
33
}
好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。
1
public class Test {
2
public static void main(String[] args) {
3
Buffer buff = new Buffer();
4
5
final Writer writer = new Writer(buff);
6
final Reader reader = new Reader(buff);
7
8
writer.start();
9
reader.start();
10
11
new Thread(new Runnable() {
12
13
@Override
14
public void run() {
15
long start = System.currentTimeMillis();
16
for (;;) {
17
//等5秒钟去中断读
18
if (System.currentTimeMillis()
19
- start > 5000) {
20
System.out.println("不等了,尝试中断");
21
reader.interrupt();
22
break;
23
}
24
25
}
26
27
}
28
}).start();
29
30
}
31
}
我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。
查看源代码打印帮助
1
import java.util.concurrent.locks.ReentrantLock;
2
3
public class BufferInterruptibly {
4
5
private ReentrantLock lock = new ReentrantLock();
6
7
public void write() {
8
lock.lock();
9
try {
10
long startTime = System.currentTimeMillis();
11
System.out.println("开始往这个buff写入数据…");
12
for (;;)// 模拟要处理很长时间
13
{
14
if (System.currentTimeMillis()
15
- startTime > Integer.MAX_VALUE)
16
break;
17
}
18
System.out.println("终于写完了");
19
} finally {
20
lock.unlock();
21
}
22
}
23
24
public void read() throws InterruptedException {
25
lock.lockInterruptibly();// 注意这里,可以响应中断
26
try {
27
System.out.println("从这个buff读数据");
28
} finally {
29
lock.unlock();
30
}
31
}
32
33
}
当然,要对reader和writer做响应的修改
查看源代码打印帮助
1
public class Reader extends Thread {
2
3
private BufferInterruptibly buff;
4
5
public Reader(BufferInterruptibly buff) {
6
this.buff = buff;
7
}
8
9
@Override
10
public void run() {
11
12
try {
13
buff.read();//可以收到中断的异常,从而有效退出
14
} catch (InterruptedException e) {
15
System.out.println("我不读了");
16
}
17
18
System.out.println("读结束");
19
20
}
21
22
}
23
24
/**
25
* Writer倒不用怎么改动
26
*/
27
public class Writer extends Thread {
28
29
private BufferInterruptibly buff;
30
31
public Writer(BufferInterruptibly buff) {
32
this.buff = buff;
33
}
34
35
@Override
36
public void run() {
37
buff.write();
38
}
39
40
}
41
42
public class Test {
43
public static void main(String[] args) {
44
BufferInterruptibly buff = new BufferInterruptibly();
45
46
final Writer writer = new Writer(buff);
47
final Reader reader = new Reader(buff);
48
49
writer.start();
50
reader.start();
51
52
new Thread(new Runnable() {
53
54
@Override
55
public void run() {
56
long start = System.currentTimeMillis();
57
for (;;) {
58
if (System.currentTimeMillis()
59
- start > 5000) {
60
System.out.println("不等了,尝试中断");
61
reader.interrupt();
62
break;
63
}
64
65
}
66
67
}
68
}).start();
69
70
}
71
}
这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。
至于第二种情况,ReentrantLock可以与Condition的配合使用,Condition为ReentrantLock锁的等待和释放提供控制逻辑。
例如,使用ReentrantLock加锁之后,可以通过它自身的Condition.await()方法释放该锁,线程在此等待Condition.signal()方法,然后继续执行下去。await方法需要放在while循环中,因此,在不同线程之间实现并发控制,还需要一个volatile的变量,boolean是原子性的变量。因此,一般的并发控制的操作逻辑如下所示:
1
volatile boolean isProcess = false;
2
ReentrantLock lock = new ReentrantLock();
3
Condtion processReady = lock.newCondtion();
4
thread: run() {
5
lock.lock();
6
isProcess = true;
7
try {
8
while(!isProcessReady) { //isProcessReady 是另外一个线程的控制变量
9
processReady.await();//释放了lock,在此等待signal
10
}catch (InterruptedException e) {
11
Thread.currentThread().interrupt();
12
} finally {
13
lock.unlock();
14
isProcess = false;
15
}
16
}
17
}
18
}
这里只是代码使用的一段简化,下面我们看Hadoop的一段摘取的源码:
查看源代码打印帮助
1
private class MapOutputBuffer<K extends Object, V extends Object>
2
implements MapOutputCollector<K, V>, IndexedSortable {
3
...
4
boolean spillInProgress;
5
final ReentrantLock spillLock = new ReentrantLock();
6
final Condition spillDone = spillLock.newCondition();
7
final Condition spillReady = spillLock.newCondition();
8
volatile boolean spillThreadRunning = false;
9
final SpillThread spillThread = new SpillThread();
10
...
11
public MapOutputBuffer(TaskUmbilicalProtocol umbilical, JobConf job,
12
TaskReporter reporter
13
) throws IOException, ClassNotFoundException {
14
...
15
spillInProgress = false;
16
spillThread.setDaemon(true);
17
spillThread.setName("SpillThread");
18
spillLock.lock();
19
try {
20
spillThread.start();
21
while (!spillThreadRunning) {
22
spillDone.await();
23
}
24
} catch (InterruptedException e) {
25
throw new IOException("Spill thread failed to initialize", e);
26
} finally {
27
spillLock.unlock();
28
}
29
}
30
31
protected class SpillThread extends Thread {
32
33
@Override
34
public void run() {
35
spillLock.lock();
36
spillThreadRunning = true;
37
try {
38
while (true) {
39
spillDone.signal();
40
while (!spillInProgress) {
41
spillReady.await();
42
}
43
try {
44
spillLock.unlock();
45
sortAndSpill();
46
} catch (Throwable t) {
47
sortSpillException = t;
48
} finally {
49
spillLock.lock();
50
if (bufend < bufstart) {
51
bufvoid = kvbuffer.length;
52
}
53
kvstart = kvend;
54
bufstart = bufend;
55
spillInProgress = false;
56
}
57
}
58
} catch (InterruptedException e) {
59
Thread.currentThread().interrupt();
60
} finally {
61
spillLock.unlock();
62
spillThreadRunning = false;
63
}
64
}
65
}
代码中spillDone 就是 spillLock的一个newCondition()。调用spillDone.await()时可以释放spillLock锁,线程进入阻塞状态,而等待其他线程的 spillDone.signal()操作时,就会唤醒线程,重新持有spillLock锁。
这里可以看出,利用lock可以使我们多线程交互变得方便,而使用synchronized则无法做到这点。
最后呢,ReentrantLock这个类还提供了2种竞争锁的机制:公平锁和非公平锁。这2种机制的意思从字面上也能了解个大概:即对于多线程来说,公平锁会依赖线程进来的顺序,后进来的线程后获得锁。而非公平锁的意思就是后进来的锁也可以和前边等待锁的线程同时竞争锁资源。对于效率来讲,当然是非公平锁效率更高,因为公平锁还要判断是不是线程队列的第一个才会让线程获得锁。
引用
在分布式开发中,锁是线程控制的重要途径。Java为此也提供了2种锁机制,synchronized和lock。做为Java爱好者,自然少不了对比一下这2种机制,也能从中学到些分布式开发需要注意的地方。
我们先从最简单的入手,逐步分析这2种的区别。
一、synchronized和lock的用法区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。
lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
用法区别比较简单,这里不赘述了,如果不懂的可以看看Java基本语法。
二、synchronized和lock性能区别
synchronized是托管给JVM执行的,而lock是java写的控制锁的代码。在Java1.5中,synchronize是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用Java提供的Lock对象,性能更高一些。但是到了Java1.6,发生了变化。synchronize在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在Java1.6上synchronize的性能并不比Lock差。官方也表示,他们也更支持synchronize,在未来的版本中还有优化余地。
说到这里,还是想提一下这2中机制的具体区别。据我所知,synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁。独占锁意味着其他线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低。
而Lock用的是乐观锁方式。所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。乐观锁实现的机制就是CAS操作(Compare and Swap)。我们可以进一步研究ReentrantLock的源代码,会发现其中比较重要的获得锁的一个方法是compareAndSetState。这里其实就是调用的CPU提供的特殊指令。
现代的CPU提供了指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
我也只是了解到这一步,具体到CPU的算法如果感兴趣的读者还可以在查阅下,如果有更好的解释也可以给我留言,我也学习下。
三、synchronized和lock用途区别
synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
下面细细道来……
先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵)
这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下:
查看源代码打印帮助
1
public class Buffer {
2
3
private Object lock;
4
5
public Buffer() {
6
lock = this;
7
}
8
9
public void write() {
10
synchronized (lock) {
11
long startTime = System.currentTimeMillis();
12
System.out.println("开始往这个buff写入数据…");
13
for (;;)// 模拟要处理很长时间
14
{
15
if (System.currentTimeMillis()
16
- startTime > Integer.MAX_VALUE)
17
break;
18
}
19
System.out.println("终于写完了");
20
}
21
}
22
23
public void read() {
24
synchronized (lock) {
25
System.out.println("从这个buff读数据");
26
}
27
}
28
}
接着,我们来定义2个线程,一个线程去写,一个线程去读。
1
public class Writer extends Thread {
2
3
private Buffer buff;
4
5
public Writer(Buffer buff) {
6
this.buff = buff;
7
}
8
9
@Override
10
public void run() {
11
buff.write();
12
}
13
14
}
15
16
public class Reader extends Thread {
17
18
private Buffer buff;
19
20
public Reader(Buffer buff) {
21
this.buff = buff;
22
}
23
24
@Override
25
public void run() {
26
27
buff.read();//这里估计会一直阻塞
28
29
System.out.println("读结束");
30
31
}
32
33
}
好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。
1
public class Test {
2
public static void main(String[] args) {
3
Buffer buff = new Buffer();
4
5
final Writer writer = new Writer(buff);
6
final Reader reader = new Reader(buff);
7
8
writer.start();
9
reader.start();
10
11
new Thread(new Runnable() {
12
13
@Override
14
public void run() {
15
long start = System.currentTimeMillis();
16
for (;;) {
17
//等5秒钟去中断读
18
if (System.currentTimeMillis()
19
- start > 5000) {
20
System.out.println("不等了,尝试中断");
21
reader.interrupt();
22
break;
23
}
24
25
}
26
27
}
28
}).start();
29
30
}
31
}
我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。
查看源代码打印帮助
1
import java.util.concurrent.locks.ReentrantLock;
2
3
public class BufferInterruptibly {
4
5
private ReentrantLock lock = new ReentrantLock();
6
7
public void write() {
8
lock.lock();
9
try {
10
long startTime = System.currentTimeMillis();
11
System.out.println("开始往这个buff写入数据…");
12
for (;;)// 模拟要处理很长时间
13
{
14
if (System.currentTimeMillis()
15
- startTime > Integer.MAX_VALUE)
16
break;
17
}
18
System.out.println("终于写完了");
19
} finally {
20
lock.unlock();
21
}
22
}
23
24
public void read() throws InterruptedException {
25
lock.lockInterruptibly();// 注意这里,可以响应中断
26
try {
27
System.out.println("从这个buff读数据");
28
} finally {
29
lock.unlock();
30
}
31
}
32
33
}
当然,要对reader和writer做响应的修改
查看源代码打印帮助
1
public class Reader extends Thread {
2
3
private BufferInterruptibly buff;
4
5
public Reader(BufferInterruptibly buff) {
6
this.buff = buff;
7
}
8
9
@Override
10
public void run() {
11
12
try {
13
buff.read();//可以收到中断的异常,从而有效退出
14
} catch (InterruptedException e) {
15
System.out.println("我不读了");
16
}
17
18
System.out.println("读结束");
19
20
}
21
22
}
23
24
/**
25
* Writer倒不用怎么改动
26
*/
27
public class Writer extends Thread {
28
29
private BufferInterruptibly buff;
30
31
public Writer(BufferInterruptibly buff) {
32
this.buff = buff;
33
}
34
35
@Override
36
public void run() {
37
buff.write();
38
}
39
40
}
41
42
public class Test {
43
public static void main(String[] args) {
44
BufferInterruptibly buff = new BufferInterruptibly();
45
46
final Writer writer = new Writer(buff);
47
final Reader reader = new Reader(buff);
48
49
writer.start();
50
reader.start();
51
52
new Thread(new Runnable() {
53
54
@Override
55
public void run() {
56
long start = System.currentTimeMillis();
57
for (;;) {
58
if (System.currentTimeMillis()
59
- start > 5000) {
60
System.out.println("不等了,尝试中断");
61
reader.interrupt();
62
break;
63
}
64
65
}
66
67
}
68
}).start();
69
70
}
71
}
这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。
至于第二种情况,ReentrantLock可以与Condition的配合使用,Condition为ReentrantLock锁的等待和释放提供控制逻辑。
例如,使用ReentrantLock加锁之后,可以通过它自身的Condition.await()方法释放该锁,线程在此等待Condition.signal()方法,然后继续执行下去。await方法需要放在while循环中,因此,在不同线程之间实现并发控制,还需要一个volatile的变量,boolean是原子性的变量。因此,一般的并发控制的操作逻辑如下所示:
1
volatile boolean isProcess = false;
2
ReentrantLock lock = new ReentrantLock();
3
Condtion processReady = lock.newCondtion();
4
thread: run() {
5
lock.lock();
6
isProcess = true;
7
try {
8
while(!isProcessReady) { //isProcessReady 是另外一个线程的控制变量
9
processReady.await();//释放了lock,在此等待signal
10
}catch (InterruptedException e) {
11
Thread.currentThread().interrupt();
12
} finally {
13
lock.unlock();
14
isProcess = false;
15
}
16
}
17
}
18
}
这里只是代码使用的一段简化,下面我们看Hadoop的一段摘取的源码:
查看源代码打印帮助
1
private class MapOutputBuffer<K extends Object, V extends Object>
2
implements MapOutputCollector<K, V>, IndexedSortable {
3
...
4
boolean spillInProgress;
5
final ReentrantLock spillLock = new ReentrantLock();
6
final Condition spillDone = spillLock.newCondition();
7
final Condition spillReady = spillLock.newCondition();
8
volatile boolean spillThreadRunning = false;
9
final SpillThread spillThread = new SpillThread();
10
...
11
public MapOutputBuffer(TaskUmbilicalProtocol umbilical, JobConf job,
12
TaskReporter reporter
13
) throws IOException, ClassNotFoundException {
14
...
15
spillInProgress = false;
16
spillThread.setDaemon(true);
17
spillThread.setName("SpillThread");
18
spillLock.lock();
19
try {
20
spillThread.start();
21
while (!spillThreadRunning) {
22
spillDone.await();
23
}
24
} catch (InterruptedException e) {
25
throw new IOException("Spill thread failed to initialize", e);
26
} finally {
27
spillLock.unlock();
28
}
29
}
30
31
protected class SpillThread extends Thread {
32
33
@Override
34
public void run() {
35
spillLock.lock();
36
spillThreadRunning = true;
37
try {
38
while (true) {
39
spillDone.signal();
40
while (!spillInProgress) {
41
spillReady.await();
42
}
43
try {
44
spillLock.unlock();
45
sortAndSpill();
46
} catch (Throwable t) {
47
sortSpillException = t;
48
} finally {
49
spillLock.lock();
50
if (bufend < bufstart) {
51
bufvoid = kvbuffer.length;
52
}
53
kvstart = kvend;
54
bufstart = bufend;
55
spillInProgress = false;
56
}
57
}
58
} catch (InterruptedException e) {
59
Thread.currentThread().interrupt();
60
} finally {
61
spillLock.unlock();
62
spillThreadRunning = false;
63
}
64
}
65
}
代码中spillDone 就是 spillLock的一个newCondition()。调用spillDone.await()时可以释放spillLock锁,线程进入阻塞状态,而等待其他线程的 spillDone.signal()操作时,就会唤醒线程,重新持有spillLock锁。
这里可以看出,利用lock可以使我们多线程交互变得方便,而使用synchronized则无法做到这点。
最后呢,ReentrantLock这个类还提供了2种竞争锁的机制:公平锁和非公平锁。这2种机制的意思从字面上也能了解个大概:即对于多线程来说,公平锁会依赖线程进来的顺序,后进来的线程后获得锁。而非公平锁的意思就是后进来的锁也可以和前边等待锁的线程同时竞争锁资源。对于效率来讲,当然是非公平锁效率更高,因为公平锁还要判断是不是线程队列的第一个才会让线程获得锁。
发表评论
-
JUC多线程的目录
2019-01-08 16:11 274引用 http://www.cnblogs.com/skywa ... -
copyOnWriteArrayList的解释和范例
2017-06-06 01:12 386http://blog.csdn.net/u011116672 ... -
concurrentHashMap实现机制
2017-05-09 10:48 446https://www.ibm.com/developerwo ... -
java多线程 这篇总结全面
2017-03-25 21:56 308http://www.jianshu.com/p/40d4c7 ... -
多线程详解
2017-01-09 02:33 374http://m635674608.iteye.com/blo ... -
黄文海 Java多线程编程模式实战指南(一):Active Object模式(上)
2016-12-08 15:43 339http://viscent.iteye.com/blog/2 ... -
java 多线程面试题
2016-07-15 11:40 371http://webcache.googleuserconte ... -
countDownLatch 与 CyclicBarrier的详解
2016-03-21 03:35 403http://blog.csdn.net/xianymo/ar ... -
java 锁
2015-08-19 10:14 374http://itindex.net/detail/42867 ... -
线程等待join
2015-08-14 17:05 342package com.test.base.thread; ...
相关推荐
《Lock详解——深入理解Java并发编程的基石》 在Java并发编程中,Lock接口及其实现是不可或缺的一部分。本文将深入探讨Lock接口的核心实现——ReentrantLock,以及它背后的抽象队列同步器...
redisson lock和tryLock 分布式锁简单练习
这篇文档“dbms_lock控制串行详解.docx”可能详细介绍了以上内容,并可能包括示例代码和实践经验,对于深入理解DBMS_LOCK的使用非常有帮助。学习并熟练掌握DBMS_LOCK,将有助于优化数据库的并发性能,提高系统的稳定...
在Java中,有两种主要的锁机制:synchronized和Lock。它们都是用来实现线程同步,防止数据竞争,确保并发环境下的数据一致性。 首先,synchronized是Java的关键字,由JVM直接支持,其底层实现依赖于操作系统原语,...
C++11 并发指南之Lock 详解 C++11 标准中提供了两种基本的锁类型:std::lock_guard 和 std::unique_lock。std::lock_guard 是 C++11 中定义的模板类,用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便...
`Lock`接口是锁的抽象,它提供了更加灵活和细粒度的控制。`ReentrantLock`是`Lock`接口的主要实现类,支持可重入性,即线程可以多次获取同一锁,只要在每次获取后正确释放即可。相比之下,`synchronized`关键字一旦...
C#提供了多种机制来实现线程同步,包括lock关键字、Monitor、同步事件和等待句柄以及Mutex类。这些机制的主要目标是避免竞态条件,保证并发执行的线程能够正确地访问和修改共享数据。 首先,我们来看lock关键字。...
《lock4j高性能分布式锁详解》 分布式锁是分布式系统中的关键组件,它在多节点共享资源时确保了数据的一致性和并发控制。lock4j作为一款高性能的分布式锁,为开发者提供了简单易用且高效的解决方案。本文将深入探讨...
本文将深入探讨C#中如何使用Lock和Redis分布式锁来解决并发问题,以秒杀系统为例进行阐述。 首先,让我们理解什么是并发控制。并发控制是指在多线程环境下确保数据的一致性和完整性,防止多个线程同时访问同一资源...
为了保护应用程序的资源不被破坏,为多线程程序提供了三种加锁的机制,分别是:Monitor类、Lock关键字和Mutex类。 1. lock lock实现的功能是:使后进入的线程不会中断当前的线程,而是等待当前线程结束后再继续执行...
`yarn.lock` 和 `package-lock.json` 文件都是npm和yarn包管理器为了确保项目依赖一致性而生成的文件。本篇文章将详细探讨这两个文件的作用、差异以及如何实现它们之间的相互转换。 ### `yarn.lock` 文件 `yarn....
描述中提到的“AutoHotkey脚本”是一种流行的自动化和热键定制语言,它允许用户编写脚本来改变Windows系统的各种行为。 Capslock++就是基于这个强大的工具,用户可以通过编写或修改AutoHotkey脚本来定制Capslock键的...
为了更好地理解“Library cache lock”的工作原理,我们需要了解 Oracle 数据库是如何管理 SQL 和 PL/SQL 对象的。每当 SQL 语句或 PL/SQL 代码被编译并加载到内存时,它们会被存储在共享池中的 library cache 区域...
在Oracle数据库系统中,了解和区分Latch(锁存器)和Lock(锁)是非常重要的,因为它们都是数据库并发控制的关键机制,确保数据的一致性和完整性。以下是对这两个概念的详细解释: 1. Latch(锁存器) - 目的:...
Folder Lock 是一款非常流行的文件加密和隐私保护软件,它可以帮助用户轻松地锁定、隐藏或加密其重要的文件、照片、视频等个人数据。这款软件适用于多种操作系统,包括 Windows 和 macOS,并且具有高度的安全性和...
而在主线程中,我们可以看到lock(c1)语句的使用,它将锁定c1对象,并执行DoNotLockMe方法和LockMe方法。如果我们不使用lock(c1)语句,那么在t1线程中调用LockMe方法时,可能会出现死锁或其他问题。 因此,lock(this...
本地变量(Thread Local Variables)和Lock锁是两种常见的解决并发问题的机制。它们各自有其特点和适用场景,下面将详细讨论它们的工作原理、优缺点以及效率比较。 **本地变量(Thread Local Variables)** 本地...
《Lock On 2.0 免1.1补丁详解》 Lock On是一款深受飞行模拟爱好者喜爱的游戏,它以其高度真实、细腻的飞行体验而闻名。在Lock On 2.0版本中,开发者进一步提升了游戏的图形表现力、飞行模型的真实度以及战斗系统的...