Java并发编程(七):ReadWriteLock读写锁与线程八锁

古城微笑少年丶 2023-07-18 15:29 15阅读 0赞

ReadWriteLock读写锁与线程八锁

场景

什么情况下采用读写锁?

  • 写写操作/读写操作 需要互斥
  • 读读操作 不需要互斥

如果都用独占锁,有时候很多读操作并发时其实并不需要锁住缺上了锁,导致效率低下,可以采用读写锁来代替:

  1. import java.util.Random;
  2. import java.util.concurrent.locks.ReadWriteLock;
  3. import java.util.concurrent.locks.ReentrantReadWriteLock;
  4. public class TestReadWriteLock {
  5. public static void main(String[] args)
  6. {
  7. ReadWriteLockDemo rw = new ReadWriteLockDemo();
  8. new Thread(new Runnable() {
  9. @Override
  10. public void run() {
  11. try {
  12. //等一下,让一些读线程先读
  13. Thread.sleep(2);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. // 写一个 随机数
  18. rw.setNumber(new Random().nextInt(111));
  19. }
  20. },"Write:").start();
  21. // 起100个线程去读
  22. for(int i= 0;i<100;i++)
  23. {
  24. new Thread(new Runnable() {
  25. @Override
  26. public void run() {
  27. rw.getNumber();
  28. }
  29. },"Read:") .start();
  30. }
  31. }
  32. }
  33. class ReadWriteLockDemo{
  34. private int number = 0;
  35. private ReadWriteLock lock = new ReentrantReadWriteLock();
  36. //读
  37. public void getNumber(){
  38. lock.readLock().lock();
  39. try{
  40. System.out.println(Thread.currentThread().getName()+":"
  41. + number);
  42. }
  43. finally {
  44. lock.readLock().unlock();
  45. }
  46. }
  47. //写
  48. public void setNumber(int number)
  49. {
  50. lock.writeLock().lock();
  51. try
  52. {
  53. System.out.println(Thread.currentThread().getName());
  54. this.number = number;
  55. }
  56. finally {
  57. lock.writeLock().unlock();
  58. }
  59. }
  60. }

代码逻辑:

  • 一个线程去写,稍微延迟一会,先让几个线程读一下
  • 100个线程去读

实际执行结果:

  1. Read::0
  2. Read::0
  3. Read::0
  4. Read::0
  5. Write:
  6. Read::23
  7. Read::23
  8. Read::23
  9. Read::23
  10. ......//后面全是Read::23

刚开始写线程还没写,读线程先读了4个0,后面写锁抢占,写入了数据,释放锁

释放之后,其他读线程继续读,读到的都是23

解决的问题

用读写锁代替独占锁,提高了效率

线程八锁

看以下代码:

  1. /** * 题目:判断打印的 "one" or "two" * * 1.两个普通同步方法,两个线程,标准打印 one two * 2.新增 Thread.sleep()方法给 getOne ,打印 one two * 3.新增 普通方法 getThree(),打印 three one two * 4.两个普通的同步方法,两个Number对象,打印 two one * 5.修改getOne为静态同步方法,打印 two one * 6.修改两个方法均为静态同步方法,一个Number对象,打印 one two * 7.一个静态同步方法,一个非静态同步方法,两个Number对象,打印 two one * 8.两个静态同步方法,两个Number对象,打印 one two * */
  2. public class TestThread8Monitor {
  3. public static void main(String[] args)
  4. {
  5. Number number = new Number();
  6. Number number2 = new Number();
  7. new Thread(() -> number.getOne()).start();
  8. new Thread(()->number2.getTwo()).start();
  9. // new Thread(()->number2.getTwo()).start();
  10. // new Thread(()->number.getThree()).start();
  11. }
  12. }
  13. class Number{
  14. public static synchronized void getOne(){
  15. try {
  16. Thread.sleep(3000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. System.out.println("one");
  21. }
  22. public static synchronized void getTwo(){
  23. System.out.println("two");
  24. }
  25. public synchronized void getThree(){
  26. System.out.println("three");
  27. }
  28. }

代码逻辑和执行结构都写在上面了

发生了什么?

关键点

  • 非静态方法的锁 默认为this,即锁住了当前的类对象
  • 静态方法的锁 为对应的Class 实例,即锁住了对应类的对象
  • 某一个时刻内,只能有一个线程持有锁,无论几个方法

由于两个number对象都是由Number的class类的实例对象产生的,所以如果锁住了class类的实例对象,其他任何number对象都无法获取锁;

如果锁住了this,即number锁住了number对象,number2锁住了number2对象,那么就互不影响了;

同理锁住this和锁住class类的实例对象也互不影响

发表评论

表情:
评论列表 (有 0 条评论,15人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ReadWriteLock

    现实中有这样一种场景:对共享资源有读和写的操作,且写操作没有读操作那么频繁。在没有写操作的时候,多个线程同时读一个资源没有任何问题,所以应该允许多个线程同时读取共享资源;但是如

    相关 ReadWriteLock

    首先对于一个技术,存在就是为了解决某些技术难点。 为什么已经有ReentLock锁,却还要引入读写锁呢? 答案就是为了解决在 读多写少的场景下的性能问题,运用读写锁,能提高