Java并发编程之重入锁与读写锁

 更新时间:2017年09月25日 15:19:29   作者:麦田  
这篇文章主要介绍了Java并发编程之重入锁与读写锁,文中相关实例代码详细,测试可用,具有一定参考价值,需要的朋友可以了解下。

Java技术迷

重入锁

重入锁,顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。重进入是指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,该特性的实现需要解决以下两个问题。

1、线程再次获取锁。锁需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。

2、锁的最终释放。线程重复n次获取了锁,随后在第n次释放该锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁被释放时,计数自减,当计数等于0时表示锁已经成功释放。

Java里面内置锁(synchronize)和Lock(ReentrantLock)都是可重入的

synchronized 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.home;
public class SynchronizedTest implements Runnable {
  public synchronized void method1() {
    System.out.println("method1获得锁,正常运行!");
    method2();
  }
  public synchronized void method2() {
    System.out.println("method2获得锁,也正常运行!");
  }
  @Override
  public void run() {
    method1();
  }
  public static void main(String[] args) {
    SynchronizedTest st = new SynchronizedTest();
    new Thread(st).start();
    new Thread(st).start();
  }
}

Lock 实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.home;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest implements Runnable {
  Lock lock = new ReentrantLock();
  public void method1() {
    lock.lock();
    System.out.println("method1获得锁,正常运行!");
    method2();
    lock.unlock();
  }
  public void method2() {
    lock.lock();
    System.out.println("method2获得锁,也正常运行!");
    lock.unlock();
  }
  @Override
  public void run() {
    method1();
  }
  public static void main(String[] args) {
    LockTest lt = new LockTest();
    new Thread(lt).start();
    new Thread(lt).start();
  }
}

两个例子最后的结果都是正确的,结果如下:

1
2
3
4
method1获得锁,正常运行!
method2获得锁,也正常运行!
method1获得锁,正常运行!
method2获得锁,也正常运行!

可重入锁最大的作用是避免死锁

读写锁

读写锁维护了一对相关的锁,一个用于只读操作,一个用于写入操作。只要没有writer,读取锁可以由多个reader线程同时保持。写入锁是独占的。

可重入读写锁 ReentrantReadWriteLock

ReentrantReadWriteLock对象提供了readLock()和writeLock()方法, 用于获取读取锁和写入锁.

读取锁允许多个reader线程同时持有, 而写入锁最多只能有一个writter线程持有.

读写锁的使用场合: 读取共享数据的频率远大于修改共享数据的频率. 在上述场合下, 使用读写锁控制共享资源的访问, 可以提高并发性能.

如果一个线程已经持有了写入锁, 则可以再持有读写锁. 相反, 如果一个线程已经持有了读取锁, 则在释放该读取锁之前, 不能再持有写入锁.

可以调用写入锁的newCondition()方法获取与该写入锁绑定的Condition对象, 此时与普通的互斥锁并没有什么区别. 但是调用读取锁的newCondition()方法将抛出异常.

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.home;
import java.util.Random;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class ReadWrte {
  // 共享数据,可以多个线程读数据,只能有一个线程写数据
  private int data;
  // 创建读写锁
  ReadWriteLock rwLock = new ReentrantReadWriteLock();
  /**
   * 读数据,上读锁
   */
  public void get() {
    // 读锁
    rwLock.readLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + ",Read!");
      Thread.sleep((long) Math.random() * 1000);
      System.out.println(Thread.currentThread().getName() + " 读出的数据为:" +
        this.getData());
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      rwLock.readLock().unlock();
    }
  }
  /**
   * 写数据,上写锁
   *
   * @param data
   */
  public void put(int data) {
    // 写锁
    rwLock.writeLock().lock();
    try {
      System.out.println(Thread.currentThread().getName() + ",Write!");
      Thread.sleep((long) Math.random() * 1000);
      this.setData(data);
      System.out.println(Thread.currentThread().getName() + " 写入的数据为:" +
        this.getData());
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      rwLock.writeLock().unlock();
    }
  }
  public int getData() {
    return data;
  }
  public void setData(int data) {
    this.data = data;
  }
}
/**
 * 测试类
 *
 * @author itmyhome
 *
 */
public class ReadWriteLockTest {
  /**
   * @param args
   */
  public static void main(String[] args) {
    // 创建ReadWrte对象
    final ReadWrte rw = new ReadWrte();
    for (int i = 0; i < 10; i++) {
      // 创建并启动10个读线程
      new Thread(new Runnable() {
          @Override
          public void run() {
            rw.get();
          }
        }).start();
      // 创建并启动10个写线程
      new Thread(new Runnable() {
          @Override
          public void run() {
            // 写入一个随机数
            rw.put(new Random().nextInt(8));
          }
        }).start();
    }
  }
}

输出为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Thread-0,Read!
Thread-4,Read!
Thread-8,Read!
Thread-12,Read!
Thread-0 读出的数据为:0
Thread-4 读出的数据为:0
Thread-8 读出的数据为:0
Thread-12 读出的数据为:0
Thread-19,Write!
Thread-19 写入的数据为:5
Thread-7,Write!
Thread-7 写入的数据为:7
Thread-3,Write!
Thread-3 写入的数据为:4
Thread-16,Read!
Thread-16 读出的数据为:4
Thread-11,Write!
Thread-11 写入的数据为:0
Thread-15,Write!
Thread-15 写入的数据为:5
Thread-2,Read!
Thread-2 读出的数据为:5
Thread-17,Write!
Thread-17 写入的数据为:2
Thread-6,Read!
Thread-6 读出的数据为:2
Thread-1,Write!
Thread-1 写入的数据为:5
Thread-13,Write!
Thread-13 写入的数据为:4
Thread-9,Write!
Thread-9 写入的数据为:7
Thread-5,Write!
Thread-5 写入的数据为:2
Thread-10,Read!
Thread-10 读出的数据为:2
Thread-18,Read!
Thread-14,Read!
Thread-18 读出的数据为:2
Thread-14 读出的数据为:2

从图中我们可以看出,可以多个线程同时读,但只能一个线程写,即写数据和写入数据一并完成。

总结

以上就是本文关于Java并发编程之重入锁与读写锁的全部内容,希望对大家有所帮助。欢迎各位参阅本站其他专题,有什么问题可以随时留言,小编会及时回复大家的。感谢大家对本站的支持。

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:http://blog.csdn.net/itmyhome1990/article/details/75309367

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • mybatis实现mapper代理模式的方式

    mybatis实现mapper代理模式的方式

    本文向大家讲解mybatis的mapper代理模式,以根据ide值查询单条数据为例编写xml文件,通过mapper代理的方式进行讲解增删改查,分步骤给大家讲解的很详细,对mybatis mapper代理模式相关知识感兴趣的朋友一起看看吧
    2021-06-06
  • 微信APP支付(IOS手机端+java后台)版

    微信APP支付(IOS手机端+java后台)版

    这篇文章主要为大家详细介绍了微信APP支付(IOS手机端+java后台)版,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Leetcode常见链表问题及代码示例

    Leetcode常见链表问题及代码示例

    这篇文章主要介绍了Leetcode常见链表问题及代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • java数据结构实现顺序表示例

    java数据结构实现顺序表示例

    这篇文章主要介绍了java数据结构实现顺序表示例,需要的朋友可以参考下
    2014-03-03
  • java中BigDecimal用法详解

    java中BigDecimal用法详解

    本文详细讲解了java中BigDecimal的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • Java集合框架LinkedList详解及实例

    Java集合框架LinkedList详解及实例

    这篇文章主要介绍了Java集合框架LinkedList详解及实例的相关资料,从定义,概述,用法进行介绍,需要的朋友可以参考下
    2017-04-04
  • Springboot中使用lombok的@Data注解方式

    Springboot中使用lombok的@Data注解方式

    这篇文章主要介绍了Springboot中使用lombok的@Data注解方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • 一步步教你把SpringBoot项目打包成Docker镜像

    一步步教你把SpringBoot项目打包成Docker镜像

    Docker可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化,下面这篇文章主要给大家介绍了关于SpringBoot项目打包成Docker镜像的相关资料,需要的朋友可以参考下
    2023-02-02
  • ant打包jar文件脚本分享

    ant打包jar文件脚本分享

    本文介绍的ant脚本是用来打包jar文件,做完JAVA应用一定会用到这个,需要的朋友可以参考下
    2014-03-03
  • Java实现断点下载服务端与客户端的示例代码

    Java实现断点下载服务端与客户端的示例代码

    这篇文章主要为大家介绍了如何实现服务端(Spring Boot)与客户端(Android)的断点下载与下载续传功能,文中的示例代码讲解详细,需要的可以参考一下
    2022-08-08

最新评论