不要同时使用ReentrantLock类与synchronized关键字锁定会修改同一个资源的不同方法

转自 http://agrael.iteye.com/blog/685840

本文是讲述ReentrantLock类与synchronized关键字同时使用的问题,不是ReentrantLock类与synchronized关键字的教程。 
    synchronized关键字作为java多线程编程中非常重要的关键字之一,它维护这线程并发中的安全。通常使用synchronized有2种方式。 
锁定当前实例

Java代码  

  1. //通过方法上使用synchronized达到锁定效果
  2. public synchronized void xxx() {
  3. //...
  4. }
  5. //通过锁定指定的实例达到锁定效果
  6. public void yyy(){
  7. synchronized (this) {
  8. //...
  9. }
  10. }
  11. public void zzz(){
  12. synchronized (xObject) {
  13. //...
  14. }
  15. }

其中第一种和第二种都是对当前方法属于的对象实例的琐定,而第三种为锁定指定的实例。 
本文不打算详细讲解synchronized关键字,有关synchronized的详细说明请参考其他资料。

java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。ReentrantLock作为Lock接口的实现,定义了可重入锁。根据API的说明:“一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。”可以发现,ReentrantLock的最基本的作用就是实现了使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义。 
ReentrantLock提供的方法比较多,但这里我们只讨论实现synchronized相同功能的方式。 
API中所使用的示例为:

Java代码  

  1. class X {
  2. private final ReentrantLock lock = new ReentrantLock();
  3. // ...
  4. public void m() {
  5. lock.lock();  // block until condition holds
  6. try {
  7. // ... method body
  8. } finally {
  9. lock.unlock()
  10. }
  11. }
  12. }

可以看出,如果上面代码换成synchronized的话,应该是:

Java代码  

  1. public synchronized void m() {
  2. // ... method body
  3. }

或者

Java代码  

  1. public void m() {
  2. synchronized (this) {
  3. // ... method body
  4. }
  5. }

锁定的是当前的实例。这也是本文的重点。(关于ReentrantLock的更多信息,请参考其他资料) 
    既然ReentrantLock和synchronized都提供了相同的行为(这里不讨论性能问题),那么在使用过程中,对于线程并发编程,使用ReentrantLock与synchronized都是可以的,他们也都可以工作得很好。但是,如果同时使用它们两个呢?结果又会是怎么样呢? 
看如下代码:

Java代码  

  1. package cn.agrael.test.thread;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. public class ReentrantLockAndSynchronized {
  4. private final ReentrantLock lock = new ReentrantLock();
  5. private volatile int i = 0;
  6. public void lockAdd() throws Exception {
  7. lock.lock();
  8. try {
  9. check("lockAdd");
  10. i++;
  11. i++;
  12. } finally {
  13. lock.unlock();
  14. }
  15. }
  16. public synchronized void synchronizedAdd() throws Exception {
  17. check("synchronizedAdd");
  18. i++;
  19. i++;
  20. }
  21. //  public void synchronizedAdd() throws Exception {
  22. //      lock.lock();
  23. //      try {
  24. //          check("lockAdd");
  25. //          i++;
  26. //          i++;
  27. //      } finally {
  28. //          lock.unlock();
  29. //      }
  30. //  }
  31. private void check(String methodName) throws Exception {
  32. if (i % 2 != 0) {
  33. throw new Exception(methodName + " : " + i);
  34. }
  35. }
  36. public static void main(String[] args) throws Exception {
  37. final ReentrantLockAndSynchronized add = new ReentrantLockAndSynchronized();
  38. Thread thread1 = new Thread(new Runnable() {
  39. public void run() {
  40. try {
  41. while (true) {
  42. add.lockAdd();
  43. }
  44. } catch (Exception e) {
  45. e.printStackTrace();
  46. System.exit(0);
  47. }
  48. }
  49. });
  50. Thread thread2 = new Thread(new Runnable() {
  51. public void run() {
  52. try {
  53. while (true) {
  54. add.synchronizedAdd();
  55. }
  56. } catch (Exception e) {
  57. e.printStackTrace();
  58. System.exit(0);
  59. }
  60. }
  61. });
  62. thread1.start();
  63. thread2.start();
  64. }
  65. }

其中有个int型的i变量,并提供了使用ReentrantLock锁定的lockAdd方法与使用synchronized锁定的synchronizedAdd方法,这2个方法都提供相同的操作,先验证i是否为偶数,如果不是则抛出异常,并且提供2次i++的操作。java中的i++并非原子性的操作,会涉及读和写,再者提供2次i++,如果是这样的话,会出现并发问题,所以我们提供了ReentrantLock以及synchronized来锁定,保证线程安全。如果我们的想法可行,那么i永远被读到的结果都是偶数,也就不永远不会抛我们所指定的异常。但是结果却不是这样,运行一会后,就抛出了异常,证明我们的想法失败了。因为ReentrantLock与synchronized所提供的机制不同,导致了他们是相对独立的,相当于是两把锁,各自锁定各自的。 
    所以最后我们下的结论就是不要同时使用ReentrantLock类与synchronized关键字锁定会修改同一个资源的不同方法。

时间: 2024-10-11 07:44:02

不要同时使用ReentrantLock类与synchronized关键字锁定会修改同一个资源的不同方法的相关文章

Java synchronized 关键字详解

Java synchronized 关键字详解 前置技能点 进程和线程的概念 线程创建方式 线程的状态状态转换 线程安全的概念 synchronized 关键字的几种用法 修饰非静态成员方法 synchronized public void sync(){ } 修饰静态成员方法 synchronized public static void sync(){ } 类锁代码块 synchronized (类.class){ } 对象锁代码块 synchronized (this|对象){ } syn

Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语

Java 5 引入的 Concurrent 并发库软件包中,提供了 ReentrantLock 可重入同步锁,用来替代 synchronized 关键字原语,并可提供更好的性能,以及更强大的功能.使用方法也很简单: public final ReentrantLock lock=new ReentrantLock(); ...... try { lock.lock(); // 进入同步内容 .... } finally { lock.unlock(); // 必须在 finally 块中解锁,否

synchronized关键字以及实例锁 类锁

Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行.另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 二.然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块.

synchronized关键字以及对象锁和类锁的区别

java并发编程中最长用到的关键字就是synchronized了,这里讲解一下这个关键字的用法和容易混淆的地方.synchronized关键字涉及到锁的概念, 在java中,synchronized锁大家又通俗的称为:方法锁,对象锁 和 类锁 三种. 先上结论! 1 无论是修饰方法还是修饰代码块都是 对象锁,当一个线程访问一个带synchronized方法时,由于对象锁的存在,所有加synchronized的方法都不能被访问(前提是在多个线程调用的是同一个对象实例中的方法)2 无论是修饰静态方法

Java对象锁和类锁全面解析(多线程synchronized关键字)

最近工作有用到一些多线程的东西,之前吧,有用到synchronized同步块,不过是别人怎么用就跟着用,并没有搞清楚锁的概念.最近也是遇到一些问题,不搞清楚锁的概念,很容易碰壁,甚至有些时候自己连用没用对都不知道. 今天把一些疑惑都解开了,写篇文章分享给大家,文章还算比较全面.当然可能有小宝鸽理解得不够深入透彻的地方,如果说得不正确还望指出. 看之前有必要跟某些猿友说一下,如果看一遍没有看明白呢,也没关系,当是了解一下,等真正使用到了,再回头看. 本文主要是将synchronized关键字用法作

synchronized关键字详解

1.    把synchronized当作函数修饰符时,示例代码如下:public synchronized void method(){//-.}这也就是同步方法,那这时synchronized锁定的是哪个对象呢?他锁定的是调用这个同步方法对象.也就是说,当一个对象P1在不同的线程中执行这个同步方法时,他们之间会出现阻塞,达到同步的效果.但是这个对象所属的Class所产生的另一对象P2却能够任意调用这个被加了synchronized关键字的方法. 2.同步块,示例代码如下:public voi

面试中关于 synchronized 关键字的 5 连击

1.1 说一说自己对于 synchronized 关键字的了解synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行.另外,在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统的 Mutex Lock 来实现的,Java 的线程是映射到操作系统的原生线程之上的.如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系

关于Java中的synchronized关键字

[内容简介] 本文主要介绍Java中如何正确的使用synchronized关键字实现线程的互斥锁. [能力需求] 至少已经完整的掌握了Java的语法基础,基本的面向对象知识,及创建并启动线程. [正文] 关于synchronized关键字的使用,很多说法是“锁同一个对象”就可以确保锁是正常的,今天,有人提了一个问题,我觉得非常不错,所以与各位一起分享一下. 在这里,就不提关于线程和synchronized关键字的基本使用了,以非常传统的“银行取钱”的故事为案例,直接上代码:Ps:以下代码是直接敲

java的线程同步机制synchronized关键字的理解

线程同步:               由于同一进程的多个线程共享同一片存储空间,在带来方便的同时,也带来了访问冲突这个严重的问题.Java语言提供了专门机制以解决这种冲突,有效避免了同一个数据对象被多个线程同时访问. 需要明确的几个问题: 1)synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.如果 再细的分类,synchronized可作用于instance变量.object reference(对象引用).static函数和clas