很早就受不了 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); } }