漫衍式遭遇并发
在前面的章节,昆山软件开发,并发操纵要么产生在单个应用内,一般利用基于JVM的lock办理并发问题,要么产生在数据库,可以思量利用数据库层面的锁,而在漫衍式场景下,需要担保多个应用实例都可以或许执行同步代码,则需要做一些特另外事情,一个最典范漫衍式同步方案即是利用漫衍式锁。
漫衍式锁由许多种实现,但本质上都是雷同的,即依赖于共享组件实现锁的询问和获取,假如说单体式应用中的Monitor是由JVM提供的,那么漫衍式下Monitor即是由共享组件提供,而典范的共享组件各人其实并不生疏,包罗但不限于:Mysql,Redis,Zookeeper。同时他们也代表了三种范例的共享组件:数据库,缓存,漫衍式协调组件。基于Consul的漫衍式锁,其实和基于Zookeeper的漫衍式锁大同小异,都是借助于漫衍式协调组件实现锁,大而化之,这三种范例的漫衍式锁,道理也都差不多,只不外,锁的特性和实现细节有所差别。
Redis实现漫衍式锁
界说需求:A应用需要完成添加库存的操纵,陈设了A1,A2,A3多个实例,实例之间的操纵要担保同步。
阐明需求:显然,此时依赖于JVM的lock已经没步伐办理问题了,A1添加锁,无法担保A2,A3的同步,这种场景可以思量利用漫衍式锁应对。
成立一张Stock表,包括id,number两个字段,别离让A1,A2,A3并发对其操纵,担保线程安详。
@Entity public class Stock { @Id private String id; private Integer number; }
界说数据库会见层:
public interface StockRepository extends JpaRepository<Stock,String> { }
这一节的主角,redis漫衍式锁,利用开源的redis漫衍式锁实现:Redisson。
引入Redisson依赖:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.5.4</version> </dependency>
界说测试类:
@RestController public class StockController { @Autowired StockRepository stockRepository; ExecutorService executorService = Executors.newFixedThreadPool(10); @Autowired RedissonClient redissonClient; final static String id = "1"; @RequestMapping("/addStock") public void addStock() { RLock lock = redissonClient.getLock("redisson:lock:stock:" + id); for (int i = 0; i < 100; i++) { executorService.execute(() -> { lock.lock(); try { Stock stock = stockRepository.findOne(id); stock.setNumber(stock.getNumber() + 1); stockRepository.save(stock); } finally { lock.unlock(); } }); } } }
上述的代码使得并发产生在多个层面。其一,在应用内部,启用线程池完成库存的加1操纵,自己即是线程不安详的,其二,在多个应用之间,这样的加1操纵越发是不受约束的。若初始化id为1的Stock数量为0。别离在当地启用A1(8080),A2(8081),A3(8082)三个应用,同时并发执行一次addStock(),若线程安详,一定可以使得数据库中的Stock为300,这即是我们的检测依据。
简朴解读下上述的代码,利用redisson获取一把RLock,RLock是java.util.concurrent.locks.Lock接口的实现类,Redisson辅佐我们屏蔽Redis漫衍式锁的实现细节,利用过java.util.concurrent.locks.Lock的伴侣城市知道下述的代码可以被称得上是同步的起手范式,究竟这是Lock的java doc中给出的代码:
Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); }
而redissonClient.getLock(“redisson:lock:stock:” + id)则是以”redisson:lock:stock:” + id该字符串作痛同步的Monitor,担保了差异id之间是相互不阻塞的。
为了担保产生并发,实际测试中我插手了Thread.sleep(1000),使竞争得以产生。测试功效:
Redis漫衍式锁简直起了浸染。
锁的留意点
假如仅仅是实现一个可以或许用于demo的Redis漫衍式锁并不难,但为何各人更方向于利用开源的实现呢?主要照旧可用性和不变性,we make things work是我在写博客,写代码时紧记在脑海中的,假如然的要细究如何本身实现一个漫衍式锁,可能平时利用锁担保并发,需要有哪些留意点呢?罗列几点:阻塞,超时时间,可重入,可用性,其他特性。
阻塞