该帖已经被评为新手帖
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-07
最后修改:2010-06-07
synchronized关键字作为java多线程编程中非常重要的关键字之一,它维护这线程并发中的安全。通常使用synchronized有2种方式。 锁定当前实例 //通过方法上使用synchronized达到锁定效果 public synchronized void xxx() { //... } //通过锁定指定的实例达到锁定效果 public void yyy(){ synchronized (this) { //... } } public void zzz(){ synchronized (xObject) { //... } } 其中第一种和第二种都是对当前方法属于的对象实例的琐定,而第三种为锁定指定的实例。 本文不打算详细讲解synchronized关键字,有关synchronized的详细说明请参考其他资料。 java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。ReentrantLock作为Lock接口的实现,定义了可重入锁。根据API的说明:“一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。”可以发现,ReentrantLock的最基本的作用就是实现了使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义。 ReentrantLock提供的方法比较多,但这里我们只讨论实现synchronized相同功能的方式。 API中所使用的示例为: class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } 可以看出,如果上面代码换成synchronized的话,应该是: public synchronized void m() { // ... method body } 或者 public void m() { synchronized (this) { // ... method body } } 锁定的是当前的实例。这也是本文的重点。(关于ReentrantLock的更多信息,请参考其他资料) 既然ReentrantLock和synchronized都提供了相同的行为(这里不讨论性能问题),那么在使用过程中,对于线程并发编程,使用ReentrantLock与synchronized都是可以的,他们也都可以工作得很好。但是,如果同时使用它们两个呢?结果又会是怎么样呢? 看如下代码: package cn.agrael.test.thread; import java.util.concurrent.locks.ReentrantLock; public class ReentrantLockAndSynchronized { private final ReentrantLock lock = new ReentrantLock(); private volatile int i = 0; public void lockAdd() throws Exception { lock.lock(); try { check("lockAdd"); i++; i++; } finally { lock.unlock(); } } public synchronized void synchronizedAdd() throws Exception { check("synchronizedAdd"); i++; i++; } // public void synchronizedAdd() throws Exception { // lock.lock(); // try { // check("lockAdd"); // i++; // i++; // } finally { // lock.unlock(); // } // } private void check(String methodName) throws Exception { if (i % 2 != 0) { throw new Exception(methodName + " : " + i); } } public static void main(String[] args) throws Exception { final ReentrantLockAndSynchronized add = new ReentrantLockAndSynchronized(); Thread thread1 = new Thread(new Runnable() { public void run() { try { while (true) { add.lockAdd(); } } catch (Exception e) { e.printStackTrace(); System.exit(0); } } }); Thread thread2 = new Thread(new Runnable() { public void run() { try { while (true) { add.synchronizedAdd(); } } catch (Exception e) { e.printStackTrace(); System.exit(0); } } }); thread1.start(); thread2.start(); } } 其中有个int型的i变量,并提供了使用ReentrantLock锁定的lockAdd方法与使用synchronized锁定的synchronizedAdd方法,这2个方法都提供相同的操作,先验证i是否为偶数,如果不是则抛出异常,并且提供2次i++的操作。java中的i++并非原子性的操作,会涉及读和写,再者提供2次i++,如果是这样的话,会出现并发问题,所以我们提供了ReentrantLock以及synchronized来锁定,保证线程安全。如果我们的想法可行,那么i永远被读到的结果都是偶数,也就不永远不会抛我们所指定的异常。但是结果却不是这样,运行一会后,就抛出了异常,证明我们的想法失败了。因为ReentrantLock与synchronized所提供的机制不同,导致了他们是相对独立的,相当于是两把锁,各自锁定各自的。 所以最后我们下的结论就是不要同时使用ReentrantLock类与synchronized关键字锁定会修改同一个资源的不同方法。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-06-07
你锁了方法的执行,又没有锁变量 i
|
|
返回顶楼 | |
发表时间:2010-06-07
kimmking 写道 你锁了方法的执行,又没有锁变量 i
因为修改是在方法中执行的。 |
|
返回顶楼 | |
发表时间:2010-06-07
针对一楼的我补充下,ReentrantLock保证同步修改的同时,如果使得变量可见,是没问题的,并不需要锁定变量i。
而且,如果是synchronized,他自己也会保证可见性。 PS:这些都是并发编程中常见到的问题,如果是一个人编写,可能不会存在什么问题,如果是不同的人编写,就要注意我文中所说的了。 |
|
返回顶楼 | |
发表时间:2010-06-07
ReentrantLock synchronized 又没有使用同一个monitor,当然没有线程安全的效果
这文章post出来我第一个感觉这就是标题党 你何不研究一下 volatile ReentrantLock synchronized 这三者的区别与联系。 |
|
返回顶楼 | |
发表时间:2010-06-07
beneo 写道 ReentrantLock synchronized 又没有使用同一个monitor,当然没有线程安全的效果
这文章post出来我第一个感觉这就是标题党 你何不研究一下 volatile ReentrantLock synchronized 这三者的区别与联系。 你的回答就是我过程中所证明的东西,不是吗?这个标题也是最后的结论,何来标题党一说? 我想在实际编程中也会有人遇到这2种机制同时使用的疑问吧? volatile ReentrantLock synchronized 这三者的区别与联系,区别已经有很多资料都有讲。这篇文章就我个人觉得,涉及了一些他们的联系的问题,不是吗? |
|
返回顶楼 | |
发表时间:2010-06-07
Agrael 写道 beneo 写道 ReentrantLock synchronized 又没有使用同一个monitor,当然没有线程安全的效果
这文章post出来我第一个感觉这就是标题党 你何不研究一下 volatile ReentrantLock synchronized 这三者的区别与联系。 你的回答就是我过程中所证明的东西,不是吗?这个标题也是最后的结论,何来标题党一说? 我想在实际编程中也会有人遇到这2种机制同时使用的疑问吧? volatile ReentrantLock synchronized 这三者的区别与联系,区别已经有很多资料都有讲。这篇文章就我个人觉得,涉及了一些他们的联系的问题,不是吗? 你的做法很值得提倡,但是你文章的技术含量不是很高,这点你应该同意吧。 你说这三者联系的文章很多,但是其实真的没有那么多,Java的锁机制本来就不是那么简单就能搞清楚的,就能用中文表达出来的更是少之又少。如果你肯再往里面专研,post出一片更加优质的文章,肯定不会有那么多人认为你这篇文章是新手帖的。 我说你是标题党,因为我看过你前几次贴的帖子,感觉都很不错,这次我看到标题,怀着很大的希望进来,结果…… |
|
返回顶楼 | |
发表时间:2010-06-07
beneo 写道 Agrael 写道 beneo 写道 ReentrantLock synchronized 又没有使用同一个monitor,当然没有线程安全的效果
这文章post出来我第一个感觉这就是标题党 你何不研究一下 volatile ReentrantLock synchronized 这三者的区别与联系。 你的回答就是我过程中所证明的东西,不是吗?这个标题也是最后的结论,何来标题党一说? 我想在实际编程中也会有人遇到这2种机制同时使用的疑问吧? volatile ReentrantLock synchronized 这三者的区别与联系,区别已经有很多资料都有讲。这篇文章就我个人觉得,涉及了一些他们的联系的问题,不是吗? 你的做法很值得提倡,但是你文章的技术含量不是很高,这点你应该同意吧。 你说这三者联系的文章很多,但是其实真的没有那么多,Java的锁机制本来就不是那么简单就能搞清楚的,就能用中文表达出来的更是少之又少。如果你肯再往里面专研,post出一片更加优质的文章,肯定不会有那么多人认为你这篇文章是新手帖的。 我说你是标题党,因为我看过你前几次贴的帖子,感觉都很不错,这次我看到标题,怀着很大的希望进来,结果…… 是的,这篇帖子的技术含量并不高,因为这篇帖子最初我的定位没定位为一个技术探讨帖,我通过这篇帖子想表达的是给写程序的朋友们说明那么一个结果,这篇帖子也只是这么一个过程,并没有什么太大的技术含量。 Java的锁机制本来就不是那么简单就能搞清楚的这点我非常同意。其实我也有想,如果在这个过程中套入大量的原理在里面,可能这篇文章的质量是上去了,但是我想给尽可能对的人说明这个结果的初衷可能就实现不了了。越是复杂,理解的人就越少,而结论通常是可以用的,我是这样想的。 PS:谢谢你关注我的帖子~:) |
|
返回顶楼 | |
发表时间:2010-06-07
其实就是锁的问题吧?lock所使用的锁是自己生成的,而使用关键字其实使用的是自身作为锁,两个完全不同的锁,肯定不能互相锁定的。
|
|
返回顶楼 | |
发表时间:2010-06-07
dwbin 写道 其实就是锁的问题吧?lock所使用的锁是自己生成的,而使用关键字其实使用的是自身作为锁,两个完全不同的锁,肯定不能互相锁定的。
关键字并不是自身作为锁的。 |
|
返回顶楼 | |