JAVA 7+ 实现自动锁(AutoLock)

  • 了解自动锁

很早就受不了 java 锁的机制了,每次都需要在 finally 去解锁, 不仅代码不美观,而且很麻烦

我想能不能实现加锁之后自动解锁, 如果是C++ 可以利用析构函数实现, 但java就.......

想了想好像可以利用java7 的 try-with-resource 特性, 对象只需要实现 AutoCloseable 接口

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 抽象接口对象

// 自动锁实现类
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....
        }
    }
}

至此我们就实现了,我们假设的常用场景

  • 更高级的用法

如果我要更细粒度的锁, 不是在对象的成员中存在锁对象,怎么办.
我写一个方法, 希望可以帮助大家, 抛砖引玉, 如果可以提供更好的方式请求留言

/**
 * 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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值