java 重入锁和读写锁

重入锁

顾名思义,就是支持重进入的锁,他表示该锁能够支持一个线程对资源的重复加锁,并且支持获取锁的公平和非公平性选择。synchronized 关键字隐私支持重进入,ReentrantLock在调用lock 方法时候,已经获取到锁的线程能够再次调用lock()方法时而不被阻塞。
公平的获取锁,也就是等待时间最长的线程最优先获取锁,也可以说获取是顺序的。ReentrantLock提供一个构造函数,能够控制锁是否是公平的
以上的设计主要基于tryacquire()方法上的修改,判断是否能够获取同步状态,而之前超时获取则是在acqurie()方法上进行逻辑修改。

  1. 实现重进入
    线程再次获取锁:获取同步状态,如果发现c==0,表明目前该锁无线程使用,则通过CAS算法进行获取。当同步状态c!=0,则判断当前的线程是不是目前获取该锁的线程,如果是获取成功,并且计数++,表明重进入次数。
    锁的释放:在第n次释放该锁后,如果计数等于0 表示锁成功释放。
  2. 公平和非公平获取锁的区别
    如果一个锁是公平的,则获取锁的顺序是FIFO,非公平锁中,只要CAS设置同步状态成功,则表明获取成功。公平锁中tryAcuires()方法中判断条件多了hasQueuedProdecessor()方法,即同步对列当前结点是否有前驱节点。

实验表明,非公平锁相对公平锁的有更高的效率,因为只要获取同步状态即成功获取锁,在这个前提下,刚刚释放锁的线程再次获取到同步状态的几率非常大,极大的减小了线程切换,保证更大吞吐量。

实例:

public class LockUtil {

    private static Map<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();

    private static final String DEFAULT_CATEGORY = "default";

    public static ReentrantLock get(String key) {
        return get(DEFAULT_CATEGORY, key);
    }

    public static ReentrantLock get(String category, String key) {
        return lockMap.computeIfAbsent(category + "_" + key, k -> new ReentrantLock());
    }
}
ReentrantLock lock = LockUtil.get("catalog", catalog);
lock.lock();
try {
     if (!factoryRepo.containsKey(catalog)) {
         ConnectorServiceFactory originFactory =
         connectorPlugin.getServiceFactory(catalogInfo);
         ConnectorServiceFactoryProxy proxyFactory = new ConnectorServiceFactoryProxy(lastUseTime, originFactory);
         factoryRepo.putIfAbsent(catalog, proxyFactory);
                        proxyFactory.open();
                    }
} finally {
        lock.unlock();
}

###读写锁
ReentrantLock等基本都是排它锁,这些锁在同意时候只允许一个线程进行访问,而读写锁同一时刻可以允许多个读线程访问,在写进程访问时候,所有的读线程和其他写进程被阻塞。
读写锁的性能比排它锁好,因为大多数场景读是多余写的,因此提供更好的并发性和吞吐量。Java 5以后提供读写锁,目前java包中提供读写锁的是现实ReentrantReadWriteLock,支持公平性选择,重进入,锁降级【获取写锁、获取读锁、再释放写锁,写锁就降级成为读锁】等。
ReadWriteLock定义读锁和写锁两个方法readLock()方法和writeLock()方法和一些获取内部状态的方法,getReadlockCount()…读书一共被获取次数,getReadHoldCount()…当前线程获取读数的次数.

读写锁的实现分析
读写状态设计:在一个整形变量上维护多种状态,就需要”按位切割使用“,高16位表示读,低16位表示写。

1.写锁的获取
写锁是***支持重进入排它锁***,当前线程在获取写锁时候,读锁被获取或则该线程不是获取写锁的线程,tryAcquire(){…if(c!=0)【//存在锁//】if(w0 || currrent!=getExclusiveOwnerThread())【//w0,没写锁则存在读锁,当前线程不是获取写锁的进程},返回false 则进入等待状态。
2.读锁的获取
读锁是***支持重进入的共享锁***,在没有其他写现场访问时候,读书总会被成功的获取,所做的是增加读状态,若被其他写进程获取,则返回-1,进入等待状态。

实例:写锁的获取与释放


        protected final boolean tryAcquire(int acquires) {
            // 获取当前线程
            Thread current = Thread.currentThread();
            // 获取ReentrantReadWriteLock锁整体同步状态
            int c = getState();
            // 获取写锁同步状态
            int w = exclusiveCount(c);
            // 存在读锁或写锁
            if (c != 0) {
                // c != 0 && w == 0 即若存在读锁或写锁持有线程不是当前线程,获取写锁失败
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                // 最多65535次重入,若超过报错
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // 可重入,设置同步状态
                setState(c + acquires);
                return true;
            }
            // 公平与非公平,同步队列是否有节点,同时cas设置状态
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            // 设置获取锁的线程为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值