本文从以下几个方面介绍Lock接口,分别从lock锁的介绍、为什么要用lock锁、方法介绍、可见性保证
Lock接口简介
Lock锁是一种工具,用来控制对共享资源的访问。
Lock锁和Synchronized锁两者各个有各自的使用场景,lock并不是来替换Synchronized锁的。
Lock接口最常见的实现类是ReentrantLock
Lock接口方法
lock接口中包含如下四个方法,分别是:lock()、tryLock()、tryLock(long time,TimeUnit unit)和lockInterruptibly(),接下来会详细介绍这四种方法是如何使用的,见下所示。
lock method
lock方法是普通的获取锁的方法,lock方法不会像synchronized方法一样在异常发生时自动释放锁,lock方法需要手动释放锁。所以在使用lock锁时一定要手动释放锁。
见下面的实现,如下
package com.yang.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * lock不会自动释放锁,我们采用try -finally的方式实现,必须手动释放锁 */ public class MustUnlock { private static Lock lock=new ReentrantLock(); public static void main(String[] args) { lock.tryLock(); try{ //制定锁定的资源,我们可以采用模拟的方式实现 System.out.println(Thread.currentThread().getName()); }finally { lock.unlock(); } } }
使用lock方法时,当产生死锁时,会永久等待,即为lock锁的弊端。所有有个tryLock的方法,接下来我们一起看下tryLock方法
tryLock method
tryLock此方法用来尝试获取锁,如果当前锁没有被其他线程占用的时候,才是可以获取成功的,该方法的返回值为True,若返回False则说明锁没有获取成功
tryLock(long time,TimeUnit unit) method
此方法相比上面方法多了尝试锁的等待时间,下实例模拟两个线程获取两把锁,见下代码。
package com.yang.lock; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 尝试获取锁,若能获取到锁,则返回true,否则返回false * 用来避免死锁问题 * 某次的运行结果如下: * 线程2取到了锁1 * 线程1取到了锁1 * 线程1获取锁2失败了,已经重试了 * 线程2获取到了锁2 * 线程2成功获取了两把锁 * 线程1取到了锁1 * 线程1获取到了锁2 * 线程1成功获取了两把锁 * * Process finished with exit code 0 */ public class TryDeadLock implements Runnable { private static Lock lock1 = new ReentrantLock(); private static Lock lock2 = new ReentrantLock(); int flag = 1; public static void main(String[] args) { TryDeadLock tryDeadLock1=new TryDeadLock(); tryDeadLock1.flag=0; TryDeadLock tryDeadLock2=new TryDeadLock(); tryDeadLock2.flag=1; new Thread(tryDeadLock1).start(); new Thread(tryDeadLock2).start(); } @Override public void run() { //模拟实现死锁 for (int i = 0; i < 100; i++) { if (flag == 1) { try { if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) { try { System.out.println("线程1取到了锁1"); Thread.sleep(new Random().nextInt(1000)); if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) { try { System.out.println("线程1获取到了锁2"); System.out.println("线程1成功获取了两把锁"); break; } finally { lock2.unlock(); } } else { System.out.println("线程1获取锁2失败了,已经重试了"); } } finally { lock1.unlock(); Thread.sleep(new Random().nextInt(1000)); } } else { System.out.println("线程1获取锁1失败了,已经重试了"); } } catch (Exception e) { } finally { } } else { try { if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) { try { System.out.println("线程2取到了锁1"); Thread.sleep(new Random().nextInt(1000)); if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) { try { System.out.println("线程2获取到了锁2"); System.out.println("线程2成功获取了两把锁"); break; } finally { lock1.unlock(); } } else { System.out.println("线程2获取锁2失败了,已经重试了"); } } finally { lock2.unlock(); Thread.sleep(new Random().nextInt(1000)); } } else { System.out.println("线程2获取锁1失败了,已经重试了"); } } catch (Exception e) { } finally { } } } } }
lockInterruptibly method
相当于把try的等待时间设置为无限时间,但是可以被中断,下文通过代码的方式去演示。如下所示。
package com.yang.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * 演示加锁的等待时间为无限制,但是是可以被中断的 */ public class LockInterruptiblyTest implements Runnable { private Lock lock = new ReentrantLock(); public static void main(String[] args) { LockInterruptiblyTest lockInterruptiblyTest=new LockInterruptiblyTest(); Thread thread0 =new Thread(lockInterruptiblyTest); Thread thread1 =new Thread(lockInterruptiblyTest); thread0.start(); thread1.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread1.interrupt(); } @Override public void run() { System.out.println(Thread.currentThread().getName() + "尝试获取锁"); try { lock.lockInterruptibly(); try { System.out.println(Thread.currentThread().getName() + "拿到了锁"); Thread.sleep(5000); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "睡眠期间中断了"); } finally { System.out.println(Thread.currentThread().getName() + "释放了锁"); } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"等锁期间被中断了"); } finally { } } }