Java并发分析—Lock

1.Lock 和 Condition

  当使用synchronied进行同步时,能够在同步代码块中只用经常使用的wait和notify等方法,在使用显示锁的时候,将经过Condition对象与任意Lock实现组合使用,为每一个对象提供多个等待方法,其中Lock代替了synchronized方法和语句的使用,Condition代替了Object监视器方法的使用,条件Condition为线程提供了一个含义,以便在某个状态条出现可能为true,另外一个线程通知它以前,一直挂起该线程,即让其等待,由于访问该共享状态信息发生在不一样的线程中,因此它必须受到保护。html

2.Lock  ReentrantLock

  Lock 接口定义了一组抽象的锁定操做。与内部锁定(intrinsic locking)不一样,Lock 提供了无条件的、可轮询的、定时的、可中断的锁获取操做,全部加锁和解锁的方法都是显式的。这提供了更加灵活的加锁机制,弥补了内部锁在功能上的一些局限——不能中断那些正在等待获取锁的线程,而且在请求锁失败的状况下,必须无限等待java

  Lock 接口主要定义了下面的一些方法,并经过ReentrantLock实现Lock 接口编程

 1 package com.test;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.Condition;
 5 import java.util.concurrent.locks.Lock;
 6 
 7 public class LockTest implements Lock{
 8 
 9     /**
10      * lock()用来获取锁。若是锁已被其余线程获取,则进行等待
11      */
12     public void lock() {}
13 
14     /**
15      * 经过这个方法去获取锁时,若是线程正在等待获取锁,则这个线程可以响应中断,即中断线程的等待状态。也就使说,当两个线程同时经过lock.lockInterruptibly()想获取某个锁时,倘若此时线程A获取到了锁,而线程B只有在等待,
16      * 那么对线程B调用threadB.interrupt()方法可以中断线程B的等待过程。
17      */
18     public void lockInterruptibly() throws InterruptedException {}
19     
20     /**
21      * 表示用来尝试获取锁,若是获取成功,则返回true,若是获取失败(即锁已被其余线程获取),则返回false,
22      * 这个方法不管如何都会当即返回。在拿不到锁时不会一直在那等待
23      */
24     public boolean tryLock() {
25         return false;
26     }
27     
28     /**
29      * 这个方法在拿不到锁时会等待必定的时间,在时间期限以内若是还拿不到锁,就返回false。
30      * 若是若是一开始拿到锁或者在等待期间内拿到了锁,则返回true
31      */
32     public boolean tryLock(long time, TimeUnit unit)
33             throws InterruptedException {
34         return false;
35     }
36     
37     /**
38      * 释放锁,必须在finally中释放
39      */
40     public void unlock() {}
41 
42     public Condition newCondition() {
43         return null;
44     }
45 }

  (1)void lock():获取锁。若是锁不可用,出于线程调度目的,将禁用当前线程,而且在得到锁以前,该线程将一直处于休眠状态。微信

  例:ide

 1 package com.test;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class Main {
 7     Lock lock = new ReentrantLock();
 8     private int i = 0;
 9 
10     public static void main(String[] args) {
11         final Main main = new Main();
12         new Thread(new Runnable() {
13             public void run() {
14                 main.write(Thread.currentThread());
15             }
16         }).start();
17         new Thread(new Runnable() {
18             public void run() {
19                 main.write(Thread.currentThread());
20             }
21         }).start();
22     }
23 
24     public void write(Thread thread) {
25         lock.lock();
26         try {
27             System.out.println(thread.getName() + "获取了锁");
28             i = 1;
29         } catch (Exception e) {
30         } finally {
31             lock.unlock();
32             System.out.println(thread.getName() + "释放了锁");
33         }
34     }
35 }

  运行结果:函数

Thread-0获取了锁
Thread-0释放了锁
Thread-1获取了锁
Thread-1释放了锁

  (2)void lockInterruptibly() throws InterruptedException:若是当前线程未被中断,则获取锁。若是锁可用,则获取锁,并当即返回。若是当前线程在获取锁时被 中断,而且支持对锁获取的中断,则将抛出InterruptedException,并清除当前线程的已中断状态。学习

  中断线程的方法参照: https://www.cnblogs.com/jenkov/p/juc_interrupt.htmlspa

  例:线程

 1 package com.test;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class InterruptionInJava extends Thread {
 7     Lock lock = new ReentrantLock();
 8     private volatile static boolean on = false;
 9 
10     public static void main(String[] args) throws InterruptedException {
11         Thread testThread = new Thread(new InterruptionInJava(), "t1");
12         Thread testThread1 = new Thread(new InterruptionInJava(), "t2");
13         testThread.start();
14         testThread1.start();
15         Thread.sleep(1000);
16         InterruptionInJava.on = true;
17         testThread.interrupt();
18     }
19 
20     @Override
21     public void run() {
22         try {
23             test(Thread.currentThread());
24         } catch (InterruptedException e) {
25             e.printStackTrace();
26         }
27     }
28 
29     public void test(Thread thread) throws InterruptedException {
30         lock.lockInterruptibly();
31         try {
32             System.out.println(thread.getName() + "获取了锁");
33             while (!on) {
34                 try {
35                     Thread.sleep(10000000);
36                 } catch (InterruptedException e) {
37                     System.out.println(Thread.currentThread().getName()
38                             + "被中断了");
39                 }
40             }
41         } finally {
42             lock.unlock();
43             System.out.println(thread.getName() + "释放了锁");
44         }
45     }
46 }

     运行结果:code

t1获取了锁
t2获取了锁
t1被中断了
t1释放了锁

  (3)boolean tryLock():若是锁可用,则获取锁,并当即返回值 true。若是锁不可用,则此方法将当即返回值 false。

  tryLolck()还可以实现可轮询查询,若是不能得到全部须要的锁,则可使用轮询的获取方式从新获取控制权,它会释放已经得到的控制权,而后从新尝试。

  例:       

 1 package com.test;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class Main {
 7     Lock lock = new ReentrantLock();
 8     private int i = 0;
 9 
10     public static void main(String[] args) {
11         final Main main = new Main();
12         new Thread(new Runnable() {
13             public void run() {
14                 try {
15                     main.write(Thread.currentThread());
16                 } catch (InterruptedException e) {
17                     e.printStackTrace();
18                 }
19             }
20         }).start();
21         new Thread(new Runnable() {
22             public void run() {
23                 try {
24                     main.write(Thread.currentThread());
25                 } catch (InterruptedException e) {
26                     System.out.println(Thread.currentThread().getName()+"线程被中断了");
27                 }
28             }
29         }).start();
30     }
31 
32     public void write(Thread thread) throws InterruptedException{
33         if(lock.tryLock()) {
34         try {
35             System.out.println(thread.getName() + "获取了锁");
36             Thread.sleep(5000);
37         } finally {
38             lock.unlock();
39             System.out.println(thread.getName() + "释放了锁");
40         }
41         }else {
42             System.out.println(thread.getName()+"当前锁不可用");
43         }
44     }
45 }

  运行结果:

Thread-0获取了锁
Thread-1当前锁不可用
Thread-0释放了锁

  (4)boolean tryLock(long time, TimeUnit unit) throws InterruptedException:若是锁在给定的等待时间内空闲,而且当前线程未被中断,则获取锁。

  当使用内部锁时,一旦开始请求,锁就不能中止,因此内部锁实现具备时限的活动带来了风险,为了解决这一问题,可以使用定时锁,当具备时限的活动调用阻塞方法,定时锁可以在时间预算内设定相应的超时,若是活动在期待的时间内没能得到结果,定时锁能使程序提早返回,可定时锁由boolean tryLock(long time, TimeUnit unit)实现。

  例:    

 1 package com.test;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.Lock;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 public class Main {
 8     Lock lock = new ReentrantLock();
 9     private int i = 0;
10 
11     public static void main(String[] args) {
12         final Main main = new Main();
13         new Thread(new Runnable() {
14             public void run() {
15                 try {
16                     main.write(Thread.currentThread());
17                 } catch (InterruptedException e) {
18                     e.printStackTrace();
19                 }
20             }
21         }).start();
22         new Thread(new Runnable() {
23             public void run() {
24                 try {
25                     main.write(Thread.currentThread());
26                 } catch (InterruptedException e) {
27                     System.out.println(Thread.currentThread().getName()+"线程被中断了");
28                 }
29             }
30         }).start();
31     }
32 
33     public void write(Thread thread) throws InterruptedException{
34         if(lock.tryLock(2000, TimeUnit.MILLISECONDS )) {
35         try {
36             System.out.println(thread.getName() + "获取了锁");
37             Thread.sleep(1500);
38         } finally {
39             lock.unlock();
40             System.out.println(thread.getName() + "释放了锁");
41         }
42         }else {
43             System.out.println(thread.getName()+"当前锁不可用");
44         }
45     }
46 } 

  运行结果:

Thread-1获取了锁
Thread-1释放了锁
Thread-0获取了锁
Thread-0释放了锁

  (5)void unlock():释放锁。

  例:把上例释放锁代码屏蔽掉  

 1 package com.test;
 2 
 3 import java.util.concurrent.TimeUnit;
 4 import java.util.concurrent.locks.Lock;
 5 import java.util.concurrent.locks.ReentrantLock;
 6 
 7 public class Main {
 8     Lock lock = new ReentrantLock();
 9     private int i = 0;
10 
11     public static void main(String[] args) {
12         final Main main = new Main();
13         new Thread(new Runnable() {
14             public void run() {
15                 try {
16                     main.write(Thread.currentThread());
17                 } catch (InterruptedException e) {
18                     e.printStackTrace();
19                 }
20             }
21         }).start();
22         new Thread(new Runnable() {
23             public void run() {
24                 try {
25                     main.write(Thread.currentThread());
26                 } catch (InterruptedException e) {
27                     System.out.println(Thread.currentThread().getName()+"线程被中断了");
28                 }
29             }
30         }).start();
31     }
32 
33     public void write(Thread thread) throws InterruptedException{
34         if(lock.tryLock(2000, TimeUnit.MILLISECONDS )) {
35         try {
36             System.out.println(thread.getName() + "获取了锁");
37             Thread.sleep(1500);
38         } finally {
39             //lock.unlock();
40             //System.out.println(thread.getName() + "释放了锁");41         }
42         }else {
43             System.out.println(thread.getName()+"当前锁不可用");
44         }
45     }
46 }

  运行结果:

Thread-1获取了锁
Thread-0当前锁不可用

  因为线程1没有释放锁,线程2在获取锁时时得不到锁的。

  (6)Condition newCondition():返回绑定到此 Lock 实例的新 Condition 实

  除以上方法外,ReentrantLock 还增长了一些高级功能,主要有如下3项:

  (1)等待可中断:当持有锁的线程长期不释放锁时,正在等待的线程能够选择放弃等待,改成处理其余事情,可中断特性对处理执行时间很是长的同步块颇有帮助。

  (2)公平锁:多个线程在等待同一个锁时必须按照申请锁的时间顺序来依次得到锁,而非公平锁不能保证这一点,在锁被释放时,任何一个等待锁的线程都有机会得到锁,synchronized中的锁时非公平的,ReentranLock默认状况下也是非公平的,但能够经过带布尔值的构造函数要求使用公平锁。

  (3)锁绑定多个条件:指一个ReentrantLock对象能够同时绑定多个Condition对象,er在synchronized中,锁对象的wait()和notify()或notifyAll()方法能够实现一个隐含的条件,若是和多余一个的条件关联的时候,就不得不额外添加一个锁,而ReentrantLock则无需这样作,只须要屡次调用newCondition()方法便可。

  例,调用 Condition.await() 将在等待前以原子方式释放锁,并在等待返回前从新获取锁。

  ReentrantLock 实现了Lock 接口。得到ReentrantLock 的锁与进入synchronized块具备相同的语义,释放 ReentrantLock 锁与退出synchronized 块有相同的语义。相比于 synchronized,ReentrantLock 提供了更多的灵活性来处理不可用的锁。

3.编程时锁的选择

  1.最好既不是用 Lock/Condition 也不使用  synchronized关键字在许多状况下,可使用java.util.concurrent包中的一种机制,它会处理全部的加锁。

  2.若是synchronized 关键字适合编写的程序,那就尽可能使用它,这样能够减小编写的代码数量,减小出错的概率,若是特别须要Lock/Condition 结构提供的独有的特性时,才是用Lock/Condition 。

参考文献:

1. http://www.javashuo.com/article/p-vdqwlxdz-hk.html

2.https://www.cnblogs.com/kylindai/archive/2006/01/24/322667.html

 3.不明来历pdf文档《Java锁机制详解》,若有侵权,请联系LZ。

 

         欢迎扫码关注个人微信公众号,或者微信公众号直接搜索Java传奇,不定时更新一些学习笔记!

                                                       

相关文章
相关标签/搜索