并发编程之重入锁和读写锁

缺乏、安全感 2022-05-14 01:08 333阅读 0赞

Java多线程中,除了sychronized关键字实现多线程之间同步互斥操作,其实还有另外一种高效的机制去完成“同步互斥”操作。即Lock对象,比synchronized关键字更为强大功能,并且有嗅探锁定,多路分支等功能。

重入锁

分为公平锁和非公平锁,默认非公平锁

  1. public ReentrantLock() {
  2. sync = new NonfairSync();
  3. }
  4. public ReentrantLock(boolean fair) {
  5. sync = fair ? new FairSync() : new NonfairSync();
  6. }
  7. private Lock lock = new ReentrantLock();
  8. public void method1(){
  9. try {
  10. //加锁
  11. lock.lock();
  12. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
  13. Thread.sleep(1000);
  14. System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. } finally {
  18. //一定要释放锁,否则其他线程拥有无法获取
  19. lock.unlock();
  20. }
  21. }

以前使用synchronized 关键字进行多线程协同工作时,需要使用Object的wait()阻塞、释放锁,notify()唤醒、不释放锁等进行配合使用。

使用Lock时可以使用新的等待/通知类,Condition一定是针对某一把固定的锁,也就是说,只有在有锁的基础上才会产生Condition。

  1. public class UseCondition {
  2. private Lock lock = new ReentrantLock();
  3. private Condition condition = lock.newCondition();
  4. public void method1(){
  5. try {
  6. lock.lock();
  7. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
  8. Thread.sleep(3000);
  9. System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
  10. // 释放锁,类似于Object.wait(),阻塞于此
  11. condition.await();
  12. System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. } finally {
  16. lock.unlock();
  17. }
  18. }
  19. public void method2(){
  20. try {
  21. lock.lock();
  22. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
  23. Thread.sleep(3000);
  24. System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
  25. // 不释放锁类,似于Object.notify()
  26. condition.signal();
  27. System.out.println("当前线程:" + Thread.currentThread().getName() + "没有释放锁");
  28. } catch (Exception e) {
  29. e.printStackTrace();
  30. } finally {
  31. lock.unlock();
  32. }
  33. }
  34. public static void main(String[] args) {
  35. final UseCondition uc = new UseCondition();
  36. Thread t1 = new Thread(new Runnable() {
  37. @Override
  38. public void run() {
  39. uc.method1();
  40. }
  41. }, "t1");
  42. Thread t2 = new Thread(new Runnable() {
  43. @Override
  44. public void run() {
  45. uc.method2();
  46. }
  47. }, "t2");
  48. t1.start();
  49. t2.start();
  50. }
  51. }

结果:
这里写图片描述

读写锁

核心思想实现读写分离,高并发下特别适合读多写少的场景。

之前的synchronized关键字和ReentrantLock同一时间只能有一个线程进行访问被锁定的代码,读写锁的机制则不是,本质上分为两把锁,读锁和写锁,在读锁情况下,多个线程可以并发访问资源,只有当是写锁时,只能一个一个的顺序执行。

口诀:读读共享,写写互斥,读写互斥。

  1. public class UseReentrantReadWriteLock {
  2. private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
  3. private ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
  4. private ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
  5. public void read() {
  6. try {
  7. readLock.lock();
  8. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
  9. Thread.sleep(3000);
  10. System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
  11. } catch (Exception e) {
  12. e.printStackTrace();
  13. } finally {
  14. readLock.unlock();
  15. }
  16. }
  17. public void write() {
  18. try {
  19. writeLock.lock();
  20. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");
  21. Thread.sleep(3000);
  22. System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. } finally {
  26. writeLock.unlock();
  27. }
  28. }
  29. public static void main(String[] args) {
  30. final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();
  31. Thread t1 = new Thread(new Runnable() {
  32. @Override
  33. public void run() {
  34. urrw.read();
  35. }
  36. }, "t1");
  37. Thread t2 = new Thread(new Runnable() {
  38. @Override
  39. public void run() {
  40. urrw.read();
  41. }
  42. }, "t2");
  43. Thread t3 = new Thread(new Runnable() {
  44. @Override
  45. public void run() {
  46. urrw.write();
  47. }
  48. }, "t3");
  49. Thread t4 = new Thread(new Runnable() {
  50. @Override
  51. public void run() {
  52. urrw.write();
  53. }
  54. }, "t4");
  55. t1.start(); // R
  56. t2.start(); // R
  57. // t1.start(); // R
  58. // t3.start(); // W
  59. t3.start(); // W
  60. t4.start(); // W
  61. }
  62. }

结果:
这里写图片描述

发表评论

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

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

相关阅读

    相关

       在java多线程中,我们知道可以用synchronize关键字来实现线程间的同步互斥工作,还有更加优秀的机制去实现同步互斥工作,Lock对象。重入锁和读写锁,他们具有比

    相关 并发编程-

    前言: 读写锁一般使用的场景是:读的操作远大于写操作,只有在这种情况下,才可以增加并发性。当写的操作大于读的操作(完全违背了读写锁的定义,后面会讲到),当频繁切换锁的话,性能