很早就受不了 java 锁的机制了,每次都须要在 finally 去解锁, 不只代码不美观,并且很麻烦java
我想能不能实现加锁以后自动解锁, 若是是C++ 能够利用析构函数实现, 但java就.......缓存
想了想好像能够利用java7 的 try-with-resource 特性, 对象只须要实现 AutoCloseable 接口ide
class AutoLock implements AutoCloseable { // other function start // ........ // other function end // I like this feature @Override public void close() throws Exception { unLock(); } }
我了解如何利用java特性写一个自动锁那么, 下面咱们开始真正的实现函数
// 自动锁实现类 public static class AutoLock implements AutoCloseable { // 重入锁对象 private ReentrantLock reentrantLock = new ReentrantLock(); /** * 自动锁 加锁 * @return 返回自动锁自己 */ public AutoLock lock() { // 加锁 reentrantLock.lock(); return this; } public static AutoLock getAutoLock() { return new AutoLock().lock(); } /** * 自动锁解锁 * @return 返回自动锁自己 */ private AutoLock unLock() { // 解锁 if (null != reentrantLock && reentrantLock.isLocked()) { reentrantLock.unlock(); } return this; } @Override public void close() throws Exception { unLock(); } }
// 简单, 调用示例 public void testAutoLock() throws Exception { try(AutoLock autoLock = new AutoLock()) { autoLock.lock() // do some thing..... } // 不用再解锁了, 不用再解锁了, 不用再解锁了!!! }
// 更方便的调用示例 public void testAutoLock() throws Exception { // 使用静态方法 try(AutoLock autoLock = AutoLock.getAutoLock()) { // do some thing..... } // 不用再解锁了, 不用再解锁了, 不用再解锁了!!! }
前面两种调用方式, 只是打个比方, 可是不少时候,咱们的需求并非 每次都须要 new ReentrantLock(), 这样并无什么N用的, 由于每次新的"重入锁"实例, 起不到防止重入的目的, 那咱们改变一下方式, 咱们作两个地方的改变, 咱们修改reentrantLock 成员不作初始化new, 而是经过参数传入Lock 抽象接口对象this
// 自动锁实现类 public class AutoLock implements AutoCloseable { // *重入锁对象 (改变1)* private Lock autoLock = null // *重写构造函数(改变2)* private AutoLock(Lock autoLock) { this.autoLock = autoLock; } /** * 自动锁 加锁 * @return 返回自动锁自己 */ public AutoLock lock() { // *加锁(改变3)* if (null != reentrantLock) { reentrantLock.lock(); } return this; } // *获取自动锁对象 (改变4)* public static AutoLock getAutoLock(Lock autoLock) { return new AutoLock(autoLock).lock(); } /** * 自动锁解锁 * @return 返回自动锁自己 */ private AutoLock unLock() { // 解锁 if (null != autoLock) { autoLock.unlock(); } return this; } @Override public void close() throws Exception { unLock(); } }
至于为何传入的是 Lock 抽象接口, 由于很所时候,咱们可能自定义一个锁对象, 或者之后JDK可能提供的其余锁, 咱们来看看调用示例吧线程
public class TestService() { private Lock reentrantLock = new ReentrantLock(); // 假设线程A调用此方法 pubilc void testAutoLockA() throws Exception { try(AutoLock autoLock = AutoLock.getAutoLock(reentrantLock)) { // do some thing.... } } // 假设线程B调用此方法 public void testAutoKLockB() throws Exception { try(AutoLock autoLock = AutoLock.getAutoLock(reentrantLock)) { // do some thing.... } } }
至此咱们就实现了,咱们假设的经常使用场景code
若是我要更细粒度的锁, 不是在对象的成员中存在锁对象,怎么办.
我写一个方法, 但愿能够帮助你们, 抛砖引玉, 若是能够提供更好的方式请求留言对象
/** * Description: TestLock * Created by: IcerLeer * Created on: 2017-08-31 17:42 */ public class LockUtils { // 自动锁缓存队列, 实现不可重入 private static ConcurrentHashMap<String, AutoLock> lockMap = new ConcurrentHashMap<>(); /** * 获取自动锁 * @param strKey 自动锁关键字 * @return 返回自动锁对象 */ public static AutoLock getAutoLock(String strKey) { synchronized (strKey.intern()) { return lockMap.computeIfAbsent(strKey, key -> new AutoLock(strKey)).lock(); } } /** * 移除自动锁 * @param strKey 自动锁关键字 */ private static void removeAutoLock(String strKey) { lockMap.remove(strKey); } /** * 自动锁 */ public static class AutoLock implements AutoCloseable { // 锁的关键字 private String lockKey = ""; // 事务锁对象 private ReentrantLock reentrantLock = new ReentrantLock(); // 引用计数 private int refNumber = 0; // 初始化构造函数 public AutoLock(String strKey) { if (StringUtils.isNotBlank(strKey)) { lockKey = strKey; } } /** * 自动锁 加锁 * @return 返回自动锁自己 */ private AutoLock lock() { // 增长引用次数 refNumber++; // 加锁 reentrantLock.lock(); return this; } /** * 自动锁解锁 * @return 返回自动锁自己 */ private AutoLock unLock() { // 解锁 if (null != reentrantLock && reentrantLock.isLocked()) { reentrantLock.unlock(); // 判断是否应该把自动锁移除队列 synchronized (lockKey.intern()) { // 减小引用次数 refNumber--; // 若是引用计数 if (0 == refNumber) { removeAutoLock(lockKey); } } } return this; } @Override public void close() throws Exception { unLock(); } } }
固然少不了调用示例接口
private void testAutoLockA() throws Exception { /// "Test" 为锁的关键字, 相同的关键字实现不可重入锁 try(LockUtils.AutoLock autoLock = LockUtils.getAutoLock("Test")) { // do some thing sleep(10); } } private void testAutoLockB() throws Exception { /// "Test" 为锁的关键字, 相同的关键字实现不可重入锁 try(LockUtils.AutoLock autoLock = LockUtils.getAutoLock("Test")) { // do some thing sleep(10); } }