资源加锁

在 Rails 当中,经常需要将某些任务作为定时任务执行,而对于系统的定时任务而言,到点就启动一个进程来处理,相互之间是独立的,这就有可能导致某一些进程同时操作某个资源,有可能导致发生出现竟态,而导致一些问题。

通常的一个思路是通过一些外部的标志来达到加锁的作用,比如说文件。

来看一段代码:

lock do
  # handle the limited resource
end

这里的 lock 就是一个加锁和解锁的过程。

lock 的一个简单实现是:

def lock
  lock_file = "resource.lock"
  return if File.exists? lock_file
  FileUtils.touch lock_file

  yield
ensure
  FileUtils.rm_rf lock_file
end

还有一种更强大更优雅的做法:

def lock(timeout = 10)
  File.open(lock_file, "w+") do |f|
    begin
      f.flock File::LOCK_EX
      Timeout::timeout(timeout) { yield }
    ensure
      f.flock File::LOCK_UN
    end
  end
end

注意

  • 其中 File::LOCK_EX 是一个排他锁,在持有这个锁期间,只能有一个进程操作该文件。File::UN 代表解锁。
  • 另外,这里还做了一个超时判断,来防止进程死锁。

enjoy!

<think>我们需要讨论在Java中资源加锁后如何保证并发性。核心是平衡锁的粒度和范围,避免不必要的串行化。 关键点包括: 1. 减少锁的持有时间 2. 减小锁的粒度(如分段锁) 3. 使用读写锁分离读/写操作 4. 使用无锁数据结构(如CAS) 5. 避免死锁 我们将分别解释并提供Java代码示例。</think>### 资源加锁后保证并发性的策略 在 Java 中,资源加锁后保证并发性的核心是**减少锁的竞争**和**优化锁的使用方式**。以下是关键策略和实现方法: #### 1. **减小锁粒度** **原理**:将一个大锁拆分为多个小锁,减少线程竞争范围 ```java // 错误做法:整个资源池使用一个大锁 class ResourcePool { private final Map<String, Resource> pool = new HashMap<>(); private final Object lock = new Object(); public Resource get(String key) { synchronized(lock) { return pool.get(key); } } } // 正确做法:分段锁(ConcurrentHashMap原理) class SegmentLockPool { private final Map<String, Resource>[] segments; private final Object[] locks; public SegmentLockPool(int concurrencyLevel) { segments = new HashMap[concurrencyLevel]; locks = new Object[concurrencyLevel]; for (int i = 0; i < concurrencyLevel; i++) { segments[i] = new HashMap<>(); locks[i] = new Object(); } } public Resource get(String key) { int segment = key.hashCode() % locks.length; synchronized(locks[segment]) { // 只锁单个分段 return segments[segment].get(key); } } } ``` #### 2. **缩短锁持有时间** **原理**:尽快释放锁,减少其他线程等待时间 ```java // 错误做法:锁内执行耗时操作 synchronized(lock) { Resource res = cache.get(key); // 1. 获取资源 processResource(res); // 2. 耗时处理(锁持有时间过长) } // 正确做法:只锁必要操作 Resource res; synchronized(lock) { res = cache.get(key); // 仅锁资源获取 } processResource(res); // 耗时操作在锁外执行 ``` #### 3. **读写分离(ReadWriteLock)** **原理**:允许多个读线程并发,写线程独占 ```java import java.util.concurrent.locks.ReentrantReadWriteLock; class ConcurrentDataStore { private final Map<String, String> data = new HashMap<>(); private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); public String read(String key) { rwLock.readLock().lock(); // 获取读锁(允许多线程并发) try { return data.get(key); } finally { rwLock.readLock().unlock(); } } public void write(String key, String value) { rwLock.writeLock().lock(); // 获取写锁(独占) try { data.put(key, value); } finally { rwLock.writeLock().unlock(); } } } ``` #### 4. **使用无锁数据结构(CAS)** **原理**:基于硬件指令实现无锁并发 ```java import java.util.concurrent.atomic.AtomicInteger; // 无锁计数器示例 class LockFreeCounter { private final AtomicInteger count = new AtomicInteger(0); public void increment() { int current; do { current = count.get(); // 读取当前值 } while (!count.compareAndSet(current, current + 1)); // CAS更新 } public int get() { return count.get(); } } ``` #### 5. **锁粗化 vs 锁消除** - **锁粗化**:合并连续锁操作(JVM自动优化) ```java // JVM会将连续synchronized合并为单个锁操作 synchronized(lock) { /* 操作1 */ } synchronized(lock) { /* 操作2 */ } // 优化后 => synchronized(lock) { /* 操作1+操作2 */ } ``` - **锁消除**:JVM移除不必要的锁(逃逸分析) ```java // 以下锁会被JVM消除(局部对象不逃逸) public void safeMethod() { Object localObj = new Object(); synchronized(localObj) { // 无实际竞争,锁被消除 System.out.println("No lock needed"); } } ``` #### 6. **避免死锁(锁顺序)** **原理**:固定锁获取顺序避免循环等待 ```java class Account { private final Object lock = new Object(); private int balance; // 转账时固定锁顺序(按账户ID排序) public static void transfer(Account from, Account to, int amount) { Object firstLock = from.lock.hashCode() < to.lock.hashCode() ? from.lock : to.lock; Object secondLock = from.lock.hashCode() < to.lock.hashCode() ? to.lock : from.lock; synchronized(firstLock) { synchronized(secondLock) { from.balance -= amount; to.balance += amount; } } } } ``` ### 性能对比表 | **策略** | 适用场景 | 并发提升幅度 | Java实现类 | |------------------|--------------------------|--------------|--------------------------| | 减小锁粒度 | 大型数据结构(Map/Set) | 高(5-10倍) | `ConcurrentHashMap` | | 读写分离 | 读多写少场景 | 高(读无限) | `ReentrantReadWriteLock` | | 无锁CAS | 计数器、状态标志 | 极高 | `AtomicInteger` | | 缩短锁持有时间 | 含IO/复杂计算的临界区 | 中等 | 代码结构优化 | | 锁粗化 | 连续小临界区 | 低 | JVM自动优化 | ### 最佳实践总结 1. **优先使用并发工具**:如 `ConcurrentHashMap`, `AtomicLong` 等 2. **读写分离**:当读操作 >> 写操作时使用 `ReadWriteLock` 3. **避免嵌套锁**:防止死锁和过度阻塞 4. **监控锁竞争**:使用JVisualVM或JFR检测锁竞争情况 5. **考虑无锁方案**:如 `Disruptor` 框架实现无锁队列 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值