谈谈java并发锁(重入锁、读写锁、公平锁)

浅浅的花香味﹌ 2023-03-02 10:53 80阅读 0赞

目录

重入锁

简单重入锁

重入锁的等待通知(Condition)

多Condition

公平锁和非公平锁

读写锁ReentrantReadWriteLock

锁优化总结:


重入锁和读写锁,他们具有比synchronized更为强大的功能,并且有嗅探锁定、多路分支等功能。

重入锁

在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。

简单重入锁

  1. //一定要在finally中解锁
  2. //感觉跟synchronized没啥区别,是对象锁。也许性能比较好一些
  3. public class UseReentrantLock {
  4. //private Lock lock = new ReentrantLock();//定义一个锁冲突
  5. public void method1(){
  6. Lock lock = new ReentrantLock();//定义两个锁不冲突
  7. try {
  8. lock.lock();
  9. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
  10. Thread.sleep(1000);
  11. System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
  12. Thread.sleep(1000);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. } finally {
  16. lock.unlock();
  17. }
  18. }
  19. public void method2(){
  20. Lock lock = new ReentrantLock();//定义两个锁不冲突
  21. try {
  22. lock.lock();
  23. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
  24. Thread.sleep(2000);
  25. System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
  26. Thread.sleep(1000);
  27. } catch (InterruptedException e) {
  28. e.printStackTrace();
  29. } finally {
  30. lock.unlock();
  31. }
  32. }
  33. public static void main(String[] args) {
  34. final UseReentrantLock ur = new UseReentrantLock();
  35. final UseReentrantLock ur2 = new UseReentrantLock();
  36. Thread t1 = new Thread(new Runnable() {
  37. @Override
  38. public void run() {
  39. ur.method1();
  40. }
  41. }, "t1");
  42. Thread t2 = new Thread(new Runnable() {
  43. @Override
  44. public void run() {
  45. ur2.method2();
  46. }
  47. }, "t2");
  48. t1.start();
  49. t2.start();
  50. try {
  51. Thread.sleep(10);
  52. } catch (InterruptedException e) {
  53. e.printStackTrace();
  54. }
  55. //System.out.println(ur.lock.getQueueLength());
  56. }
  57. }

重入锁的等待通知(Condition)

就像synchronized,wait()、notify()、notifyAll()。

同样,在使用Lock的时候,可以使用一个新的等待/通知的类,它就是Condition。这个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. condition.await(); // Object wait
  11. System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. } finally {
  15. lock.unlock();
  16. }
  17. }
  18. public void method2(){
  19. try {
  20. lock.lock();
  21. System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
  22. Thread.sleep(3000);
  23. System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
  24. condition.signal(); //Object有notifyAll,同理这里也有signalAll()方法
  25. } catch (Exception e) {
  26. e.printStackTrace();
  27. } finally {
  28. lock.unlock();
  29. }
  30. }
  31. public static void main(String[] args) {
  32. final UseCondition uc = new UseCondition();
  33. Thread t1 = new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. uc.method1();
  37. }
  38. }, "t1");
  39. Thread t2 = new Thread(new Runnable() {
  40. @Override
  41. public void run() {
  42. uc.method2();
  43. }
  44. }, "t2");
  45. t1.start();
  46. t2.start();
  47. }
  48. }

多Condition

我们可以通过一个Lock对象产生多个Condition进行多线程间的交互,非常的灵活。可以使得部分需要唤醒的线程唤醒,其他线程则继续等待通知。

  1. //两个Condition是独立的!!!
  2. public class UseManyCondition {
  3. private ReentrantLock lock = new ReentrantLock();
  4. private Condition c1 = lock.newCondition();
  5. private Condition c2 = lock.newCondition();
  6. public void m1(){
  7. try {
  8. lock.lock();
  9. System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");
  10. c1.await();
  11. System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. } finally {
  15. lock.unlock();
  16. }
  17. }
  18. public void m2(){
  19. try {
  20. lock.lock();
  21. System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");
  22. c1.await();
  23. System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. } finally {
  27. lock.unlock();
  28. }
  29. }
  30. public void m3(){
  31. try {
  32. lock.lock();
  33. System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");
  34. c2.await();
  35. System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");
  36. } catch (Exception e) {
  37. e.printStackTrace();
  38. } finally {
  39. lock.unlock();
  40. }
  41. }
  42. public void m4(){
  43. try {
  44. lock.lock();
  45. System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
  46. c1.signalAll();
  47. } catch (Exception e) {
  48. e.printStackTrace();
  49. } finally {
  50. lock.unlock();
  51. }
  52. }
  53. public void m5(){
  54. try {
  55. lock.lock();
  56. System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");
  57. c2.signal();
  58. } catch (Exception e) {
  59. e.printStackTrace();
  60. } finally {
  61. lock.unlock();
  62. }
  63. }
  64. public static void main(String[] args) {
  65. final UseManyCondition umc = new UseManyCondition();
  66. Thread t1 = new Thread(new Runnable() {
  67. @Override
  68. public void run() {
  69. umc.m1();
  70. }
  71. },"t1");
  72. Thread t2 = new Thread(new Runnable() {
  73. @Override
  74. public void run() {
  75. umc.m2();
  76. }
  77. },"t2");
  78. Thread t3 = new Thread(new Runnable() {
  79. @Override
  80. public void run() {
  81. umc.m3();
  82. }
  83. },"t3");
  84. Thread t4 = new Thread(new Runnable() {
  85. @Override
  86. public void run() {
  87. umc.m4();
  88. }
  89. },"t4");
  90. Thread t5 = new Thread(new Runnable() {
  91. @Override
  92. public void run() {
  93. umc.m5();
  94. }
  95. },"t5");
  96. t1.start(); // c1
  97. t2.start(); // c1
  98. t3.start(); // c2
  99. try {
  100. Thread.sleep(2000);
  101. } catch (InterruptedException e) {
  102. e.printStackTrace();
  103. }
  104. t4.start(); // c1
  105. try {
  106. Thread.sleep(2000);
  107. } catch (InterruptedException e) {
  108. e.printStackTrace();
  109. }
  110. t5.start(); // c2
  111. }
  112. }

公平锁和非公平锁

默认是非公平,非公平锁效率比较高。

Lock lock = new ReentrantLock(boolean isFair);//可传参,默认是false非公平锁。

lock用法:

tryLock():尝试获得锁,获得结果用true/false返回。

tryLock():在给定的时间内尝试获得锁,获得结果用true/false返回。

isFair():是否是公平锁。

isLocked():是否锁定。

getHoldCount():查询当前线程保持此锁的个数,也就是调用lock()次数。

lockInterruptibly():优先响应中断的锁。

getQueueLength():返回正在等待获取此锁的线程数。

getWaitQueueLength():返回等待与锁定相关的给定条件Condition的线程数。

hasQueuedThread(Thread thread):查询指定的线程是否在等待此锁。

hasQueuedThreads():查询是否有线程正在等待此锁。

hasWaiters():查询是否有线程正在等待与此锁定有关的condition条件。

读写锁ReentrantReadWriteLock

读写锁ReentrantReadWriteLock,其核心就是实现读写分离的锁。在高并发访问下,尤其是读多写少的情况下,性能要远高于重入锁。

之前的synchronized、ReentrantLock,我们知道,同一时间内只能有一个线程进行访问被锁定的代码,那么读写锁则不同,其本质是分成两个锁,即读锁、写锁。在读锁下,多个线程可以并发的进行访问,但是在写锁的时候,只能一个个的顺序访问。

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

  1. public class UseReentrantReadWriteLock {
  2. private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
  3. private ReadLock readLock = rwLock.readLock();
  4. private 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. }

锁优化总结:

1.避免死锁。

2.减小锁的持有时间。

3.减小锁的粒度。

4.锁的分离(读写锁)。

5.尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字。

发表评论

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

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

相关阅读