18.并发编程--锁-介绍

并发编程--锁-介绍

  • LOCK
  • ReentrantLock
  • Condition
  • ReentrantReadWriteLock

1. LOCK(计时器) 

介绍
  • 从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。
  • 既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?  

理解:

  • synchronized的缺陷
  • 一如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况
  • 1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;
  • 2)线程执行发生异常,此时JVM会让线程自动释放锁;
  • 那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能等待,试想一下,这多么影响程序执行效率。
  • 因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。
LOCK 的特点

支持重入锁,嗅探锁定,多路分支等功能,Lock接口中每个方法的使用, 1-4是用来获取锁的,5方法是用来释放锁的。

1)lock() //lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待.

2)tryLock() //tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false.

3)tryLock(long time, TimeUnit unit) //和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间, 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true

4)lockInterruptibly() 比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

5)unLock() 。

6)newCondition() LOCK 中的类似Object等待wait/通知notify的方法

7) isLocked() 是否锁定 锁定嗅探。

8) isFail() 是否是公平锁。

9) getQueueLength() 返回正在等待获得此锁定的线程数。

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

11) hasQueueThread(Thread thread) 查询指定的线程是否等待此锁。

12) hasQueueThreads() 查询是否存在线程正在等待此锁。

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

2. ReentrantLock

ReentrantLock,意思是“可重入锁”。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法.
> 注意 重入锁,在需要进行同步的代码部分加上锁,最后一定要释放锁,不然你就蛋疼了。

 1 public class MyReentrantLock {
 2   private Lock lock = new ReentrantLock();
 3 
 4   public void method1(){
 5     try {
 6       lock.lock();
 7       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");
 8       Thread.sleep(1000);
 9       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");
10       Thread.sleep(1000);
11     } catch (InterruptedException e) {
12       e.printStackTrace();
13     } finally {
14 
15       lock.unlock();//释放锁
16     }
17   }
18 
19   public void method2(){
20     try {
21       lock.lock();
22       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");
23       Thread.sleep(2000);
24       System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");
25       Thread.sleep(1000);
26     } catch (InterruptedException e) {
27       e.printStackTrace();
28     } finally {
29 
30       lock.unlock();//释放锁
31     }
32   }
33 
34   public static void main(String[] args) {
35 
36     final MyReentrantLock ur = new MyReentrantLock();
37     Thread t1 = new Thread(new Runnable() {
38       @Override
39       public void run() {
40         ur.method1();
41         ur.method2();
42       }
43     }, "t1");
44 
45     t1.start();
46     try {
47       Thread.sleep(10);
48     } catch (InterruptedException e) {
49       e.printStackTrace();
50     }
51   }
52 }

执行结果:

当前线程:t1进入method1..
当前线程:t1退出method1..
当前线程:t1进入method2..
当前线程:t1退出method2..
lock.newCondition()
Condition 的作用:newCondition() LOCK 中的类似Object等待wait/通知notify的方法。

示例:

 1 public class MyCondition {
 2   private Lock lock = new ReentrantLock();
 3   private Condition condition = lock.newCondition();
 4 
 5   public void method1(){
 6     try {
 7       lock.lock();
 8       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");
 9       Thread.sleep(3000);
10       System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");
11       condition.await();    // Object wait
12       System.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");
13     } catch (Exception e) {
14       e.printStackTrace();
15     } finally {
16       lock.unlock();
17     }
18   }
19 
20   public void method2(){
21     try {
22       lock.lock();
23       System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");
24       Thread.sleep(3000);
25       System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");
26       condition.signal();        //Object notify
27     } catch (Exception e) {
28       e.printStackTrace();
29     } finally {
30       lock.unlock();
31     }
32   }
33 
34   public static void main(String[] args) {
35     final MyCondition uc = new MyCondition();
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 
49     t1.start();
50     t2.start();
51   }
52 }  

输出结果:

1 当前线程:t1进入..
2 当前线程:t1释放锁..
3 当前线程:t2进入..
4 当前线程:t2发出唤醒..
5 当前线程:t1继续执行...
多个Condition

可以通过一个LOCK锁产生多个Condition进行线程间的交互。非常灵活,是的部分需要换新的线程唤醒,其他线程则继续等待通知。

示例:MyManyCondition.java

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

执行结果:

当前线程:t1进入方法m1等待..
当前线程:t3进入方法m3等待..
当前线程:t2进入方法m2等待..
当前线程:t4唤醒..
当前线程:t1方法m1继续..
当前线程:t2方法m2继续..
当前线程:t5唤醒..
当前线程:t3方法m3继续..

3. ReentrantReadWriteLock 

ReentrantReadWriteLock是Lock的另一种实现方式.
我们已经知道了ReentrantLock是一个排他锁,同一时间只允许一个线程访问,而ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。
在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。

ReentrantReadWriteLock支持以下功能:

支持公平和非公平的获取锁的方式;
支持可重入。读线程在获取了读锁后还可以获取读锁;写线程在获取了写锁之后既可以再次获取写锁又可以获取读锁;
还允许从写入锁降级为读取锁,其实现方式是:先获取写入锁,然后获取读取锁,最后释放写入锁。但是,从读取锁升级到写入锁是不允许的;
读取锁和写入锁都支持锁获取期间的中断;
Condition支持。仅写入锁提供了一个 Conditon 实现;读取锁不支持 Conditon ,readLock().newCondition() 会抛出 UnsupportedOperationException;

示例:MyReentrantReadWriteLock.java

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

运行结构(打开注解1):

当前线程:t2进入...
当前线程:t1进入...
当前线程:t2退出...
当前线程:t1退出...

运行结构(打开注解2):

当前线程:t1进入...
当前线程:t1退出...
当前线程:t3进入...
当前线程:t3退出...

运行结果(打开注解3):

当前线程:t3进入...
当前线程:t3退出...
当前线程:t4进入...
当前线程:t4退出...

  总结:读读共享,读写互斥,写写互斥

转载于:https://www.cnblogs.com/Mao-admin/p/9989510.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值