Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我们拿Java线程(二)中的一个例子简单的实现一下和sychronized一样的效果,代码如下:

  1. public class LockTest {
  2. public static void main(String[] args) {
  3. final Outputter1 output = new Outputter1();
  4. new Thread() {
  5. public void run() {
  6. output.output("zhangsan");
  7. };
  8. }.start();
  9. new Thread() {
  10. public void run() {
  11. output.output("lisi");
  12. };
  13. }.start();
  14. }
  15. }
  16. class Outputter1 {
  17. private Lock lock = new ReentrantLock();// 锁对象
  18. public void output(String name) {
  19. // TODO 线程输出方法
  20. lock.lock();// 得到锁
  21. try {
  22. for(int i = 0; i < name.length(); i++) {
  23. System.out.print(name.charAt(i));
  24. }
  25. } finally {
  26. lock.unlock();// 释放锁
  27. }
  28. }
  29. }

这样就实现了和sychronized一样的同步效果,需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内。

如果说这就是Lock,那么它不能成为同步问题更完美的处理方式,下面要介绍的是读写锁(ReadWriteLock),我们会有一种需求,在对数据进行读写的时候,为了保证数据的一致性和完整性,需要读和写是互斥的,写和写是互斥的,但是读和读是不需要互斥的,这样读和读不互斥性能更高些,来看一下不考虑互斥情况的代码原型:

  1. public class ReadWriteLockTest {
  2. public static void main(String[] args) {
  3. final Data data = new Data();
  4. for (int i = 0; i < 3; i++) {
  5. new Thread(new Runnable() {
  6. public void run() {
  7. for (int j = 0; j < 5; j++) {
  8. data.set(new Random().nextInt(30));
  9. }
  10. }
  11. }).start();
  12. }
  13. for (int i = 0; i < 3; i++) {
  14. new Thread(new Runnable() {
  15. public void run() {
  16. for (int j = 0; j < 5; j++) {
  17. data.get();
  18. }
  19. }
  20. }).start();
  21. }
  22. }
  23. }
  24. class Data {
  25. private int data;// 共享数据
  26. public void set(int data) {
  27. System.out.println(Thread.currentThread().getName() + "准备写入数据");
  28. try {
  29. Thread.sleep(20);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. this.data = data;
  34. System.out.println(Thread.currentThread().getName() + "写入" + this.data);
  35. }
  36. public void get() {
  37. System.out.println(Thread.currentThread().getName() + "准备读取数据");
  38. try {
  39. Thread.sleep(20);
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. }
  43. System.out.println(Thread.currentThread().getName() + "读取" + this.data);
  44. }
  45. }

部分输出结果:

  1. Thread-1准备写入数据
  2. Thread-3准备读取数据
  3. Thread-2准备写入数据
  4. Thread-0准备写入数据
  5. Thread-4准备读取数据
  6. Thread-5准备读取数据
  7. Thread-2写入12
  8. Thread-4读取12
  9. Thread-5读取5
  10. Thread-1写入12

我们要实现写入和写入互斥,读取和写入互斥,读取和读取互斥,在set和get方法加入sychronized修饰符:

  1. public synchronized void set(int data) {...}
  2. public synchronized void get() {...}

部分输出结果:

  1. Thread-0准备写入数据
  2. Thread-0写入9
  3. Thread-5准备读取数据
  4. Thread-5读取9
  5. Thread-5准备读取数据
  6. Thread-5读取9
  7. Thread-5准备读取数据
  8. Thread-5读取9
  9. Thread-5准备读取数据
  10. Thread-5读取9

我们发现,虽然写入和写入互斥了,读取和写入也互斥了,但是读取和读取之间也互斥了,不能并发执行,效率较低,用读写锁实现代码如下:

  1. class Data {
  2. private int data;// 共享数据
  3. private ReadWriteLock rwl = new ReentrantReadWriteLock();
  4. public void set(int data) {
  5. rwl.writeLock().lock();// 取到写锁
  6. try {
  7. System.out.println(Thread.currentThread().getName() + "准备写入数据");
  8. try {
  9. Thread.sleep(20);
  10. } catch (InterruptedException e) {
  11. e.printStackTrace();
  12. }
  13. this.data = data;
  14. System.out.println(Thread.currentThread().getName() + "写入" + this.data);
  15. } finally {
  16. rwl.writeLock().unlock();// 释放写锁
  17. }
  18. }
  19. public void get() {
  20. rwl.readLock().lock();// 取到读锁
  21. try {
  22. System.out.println(Thread.currentThread().getName() + "准备读取数据");
  23. try {
  24. Thread.sleep(20);
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }
  28. System.out.println(Thread.currentThread().getName() + "读取" + this.data);
  29. } finally {
  30. rwl.readLock().unlock();// 释放读锁
  31. }
  32. }
  33. }

部分输出结果:

  1. Thread-4准备读取数据
  2. Thread-3准备读取数据
  3. Thread-5准备读取数据
  4. Thread-5读取18
  5. Thread-4读取18
  6. Thread-3读取18
  7. Thread-2准备写入数据
  8. Thread-2写入6
  9. Thread-2准备写入数据
  10. Thread-2写入10
  11. Thread-1准备写入数据
  12. Thread-1写入22
  13. Thread-5准备读取数据

从结果可以看出实现了我们的需求,这只是锁的基本用法,锁的机制还需要继续深入学习。

ReentrantReadWriteLock读写锁的使用

 

  Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。

  读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

  ReentrantReadWriteLock会使用两把锁来解决问题,一个读锁,一个写锁
线程进入读锁的前提条件:
    没有其他线程的写锁,
    没有写请求或者有写请求,但调用线程和持有锁的线程是同一个

线程进入写锁的前提条件:
    没有其他线程的读锁
    没有其他线程的写锁

到ReentrantReadWriteLock,首先要做的是与ReentrantLock划清界限。它和后者都是单独的实现,彼此之间没有继承或实现的关系。然后就是总结这个锁机制的特性了: 
     (a).重入方面其内部的WriteLock可以获取ReadLock,但是反过来ReadLock想要获得WriteLock则永远都不要想。 
     (b).WriteLock可以降级为ReadLock,顺序是:先获得WriteLock再获得ReadLock,然后释放WriteLock,这时候线程将保持Readlock的持有。反过来ReadLock想要升级为WriteLock则不可能,为什么?参看(a),呵呵. 
     (c).ReadLock可以被多个线程持有并且在作用时排斥任何的WriteLock,而WriteLock则是完全的互斥。这一特性最为重要,因为对于高读取频率而相对较低写入的数据结构,使用此类锁同步机制则可以提高并发量。 
     (d).不管是ReadLock还是WriteLock都支持Interrupt,语义与ReentrantLock一致。 
     (e).WriteLock支持Condition并且与ReentrantLock语义一致,而ReadLock则不能使用Condition,否则抛出UnsupportedOperationException异常。

源码分析: http://blog.csdn.net/yuhongye111/article/details/39055531

锁对象-Lock: 同步问题更完美的处理方式 (ReentrantReadWriteLock读写锁的使用/源码分析)的更多相关文章

  1. 锁对象Lock-同步问题更完美的处理方式

    Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我 ...

  2. 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 百篇博客分析OpenHarmony源码 | v27.02

    百篇博客系列篇.本篇为: v27.xx 鸿蒙内核源码分析(互斥锁篇) | 比自旋锁丰满的互斥锁 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当立贞 ...

  3. 锁对象Lock

    Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题: public class LockTest { publicstaticv ...

  4. 构建锁与同步组件的基石AQS:深入AQS的实现原理与源码分析

    Java并发包(JUC)中提供了很多并发工具,这其中,很多我们耳熟能详的并发工具,譬如ReentrangLock.Semaphore,它们的实现都用到了一个共同的基类--AbstractQueuedS ...

  5. Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析

    原文:Redisson分布式锁学习总结:可重入锁 RedissonLock#lock 获取锁源码分析 一.RedissonLock#lock 源码分析 1.根据锁key计算出 slot,一个slot对 ...

  6. Java显式锁学习总结之六:Condition源码分析

    概述 先来回顾一下java中的等待/通知机制 我们有时会遇到这样的场景:线程A执行到某个点的时候,因为某个条件condition不满足,需要线程A暂停:等到线程B修改了条件condition,使con ...

  7. Java显式锁学习总结之五:ReentrantReadWriteLock源码分析

    概述 我们在介绍AbstractQueuedSynchronizer的时候介绍过,AQS支持独占式同步状态获取/释放.共享式同步状态获取/释放两种模式,对应的典型应用分别是ReentrantLock和 ...

  8. concurrent(六)同步辅助器CyclicBarrier & 源码分析

    参考文档:Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例:https://www.cnblogs.com/skywang12345/p/3533995.html简介Cy ...

  9. RedissonLock分布式锁源码分析

    最近碰到的一个问题,Java代码中写了一个定时器,分布式部署的时候,多台同时执行的话就会出现重复的数据,为了避免这种情况,之前是通过在配置文件里写上可以执行这段代码的IP,代码中判断如果跟这个IP相等 ...

随机推荐

  1. 解决shell命令"** is not in the sudoers file..."错误

    Linux中新建的普通用户一般不会分配给root权限,每次都su root也太麻烦,可以通过在/etc/sudoers文件中添加当前用户的方式,给当前用户赋予sudo命令的使用权限. # 切换到roo ...

  2. Qt-网易云音乐界面实现-5 收藏列表,播放列表实现 QListWidget QTableWidget

    先上目前完成的内容吧,发现后面越写越多.在看看点击量,心凉凉. 完成了左侧的导航列表,还有就是右下角的播放列表. //创建的歌单 my_Create_Music_List = new QListWid ...

  3. python自动化17-JS处理滚动条

    前言 selenium并不是万能的,有时候页面上操作无法实现的,这时候就需要借助JS来完成了. 常见场景: 当页面上的元素超过一屏后,想操作屏幕下方的元素,是不能直接定位到,会报元素不可见的. 这时候 ...

  4. RabbitMQ入门:Hello RabbitMQ 代码实例

    在之前的一篇博客RabbitMQ入门:认识并安装RabbitMQ(以Windows系统为例)中,我们安装了RabbitMQ并且对其也有的初步的认识,今天就来写个入门小例子来加深概念理解并了解代码怎么实 ...

  5. 浅谈ajax同步、异步的问题

    最近实习的时候看到过firefox的同步.异步的警告,想着概念不是那么清楚,于是整理了一下ajax同步异步方面的知识.我是小白,做个笔记. 首先就是概念问题,ajax根据async进行区分同步和异步过 ...

  6. ats缓存规则

    一. 用户访问过程:1. ats收到一个用户对web对象的请求;2. 使用该地址, ats尝试着在其对象数据库(缓存)中用被请求对象的地址来定位该对象;3. 如果对象在缓存中, ats会检查该对象是否 ...

  7. python-分叉树枝

    import turtle def draw_branch(length): #绘制右侧树枝 if length >5: if length == 10: turtle.pencolor('gr ...

  8. Doing Homework again:贪心+结构体sort

    Doing Homework again Problem Description Ignatius has just come back school from the 30th ACM/ICPC. ...

  9. React Native移动开发实战-4-Android平台的适配原理

    打开Android开发工具Android Studio,选择菜单 Open an existing AndroidStudio project,打开ch04项目的android文件夹,如图5.8所示. ...

  10. 主成分分析——PCA

    在数据挖掘过程中,当一个对象有多个属性(即该对象的测量过程产生多个变量)时,会产生高维度数据,这给数据挖掘工作带来了难度,我们希望用较少的变量来描述数据的绝大多数信息,此时一个比较好的方法是先对数据进 ...