本文主要是介绍对锁的认识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
场景
一、自己编写一个数据库连接池,涉及到共享连接对象Connection的创建以及获取数据库连接Connectin,这个场景需要考虑多个请求同时到达,是否会初始化多次,可以考虑使用单例设计模式初始化连接池,多个线程获取连接对象,可以使用锁的方式进行控制
1、一个进程管理多个线程,多个线程对成员变量的引用是存在并发访问的,因此成员变量的定义如果涉及到多线程的访问,考虑使用锁,synchronized,ReentrantLock,Lock 实现对统一咨询的保护
二、对于外卖这种方式,如果用户下单成功之后,一个商家多个服务员进行接单,生成取餐号。因此需要针对这个取餐号做好兵法的控制。具体的方案可以参考下面
1、多个请求对数据库进行更新操作,新查询,然后在更新。因此可以使用数据库的悲观锁,select * from 表 where condition=? for update 查询的时候,对行进行锁定,如果查询条件没有索引,则对整个表锁定。因此需要注意锁定的时候,是否是行级别的锁。一般情况下,使用spring的事务,TransactionTemplate事务模版进行声明式编程,
2、使用乐观锁版本控制的方式,在取餐号表里面加一个version的字段,(1)先查取这个version版本号(2)然后做业务操作(3)更新状态以及version版本号。如果出现并发操作,则一个线程会更新失败,则可以提示用户重新操作。这种方式适用失败+重拾
version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
核心SQL代码:
update table set x=x+1, version=version+1 where id=#{id} and version=#{version};
CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。
3、可以使用tair的原子操作,incr,decr原子的增+1,和减-1.然后,可以根据时间判断或者定时任务将tair数据同步到mysql数据库中
三、分布式开发中,android手机提供一个导入手机通讯录的功能,由于用户连续点击,客户端没有控制好,则会出现二个相同的请求,导致通讯录的数据出现重复。
(1)可以使用幂等表,数据库字段中加唯一性索引
(2)业务传递唯一标识,数据库对这个字段唯一索引,直接幂等
(3)使用分布式的锁,tair或者mysql,zookeeper实现,思路是在业务请求的时候,首先获取锁,然后在执行
分布式锁使用tair实现
在实际工作中,服务都是在分布式环境下,需要有一个分布式锁,来解决分布式环境下的并发问题。本文主要讲述如何用tair 实现分布式锁。
依赖pom
<dependency><groupId>com.taobao.tair</groupId><artifactId>tair-client</artifactId><version>2.3.4</version>
</dependency
1 实现思路
TairManager.put 传入version(version>0)版本进行校验,cas原则会保证只有一个能成功,tair 在第一次put时候 version=1。
具体实现代码
import com.taobao.tair.DataEntry;
import com.taobao.tair.Result;
import com.taobao.tair.ResultCode;
import com.taobao.tair.impl.DefaultTairManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;/*** Created by hsc on 17/10/18.*/
@Component
public class TairWrapper {private DefaultTairManager tairManager;@Value("${tair.master}")private String master;@Value("${tair.slave}")private String slave;@Value("${tair.groupname}")private String groupname;@Value("${tair.namespace}")private int namespace;@PostConstructprivate void init() throws Exception {tairManager = new DefaultTairManager();List<String> configserverList = new ArrayList<String>();configserverList.add(master);configserverList.add(slave);tairManager.setConfigServerList(configserverList);tairManager.setGroupName(groupname);tairManager.init();}public ResultCode put(String key, String value, int expireTime) {return tairManager.put(namespace, key, value, expireTime);}public ResultCode put(String key, String value, int version, int expireTime) {return tairManager.put(namespace, key, value, version, expireTime);}public Result<DataEntry> get(String key) {return tairManager.get(namespace, key);}public ResultCode delete(String key) {return tairManager.delete(namespace, key);}
package com.mogujie.enzo.service.tair.impl;import com.mogujie.enzo.tair.TairWrapper;
import com.taobao.tair.DataEntry;
import com.taobao.tair.Result;
import com.taobao.tair.ResultCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** Created by hsc on 17/10/18.*/
@Service("lockService")
public class LockService {/*** 锁过期时间 5s*/private static final int LOCK_EXPIRE_TIME = 5;@Resourceprivate TairWrapper tairWrapper;/*** tair 在第一次put version=1* 当 version 大于0时候,会根据version 比较,如果vesion 相等则更新* version=0 时候会更新*/private final static int INIT_VERSION = 2;private static final int lockWaitTimeOut = 5000;private static final int retryTime = 200;private static final Logger logger = LoggerFactory.getLogger(LockService.class);
//获取锁public boolean getCacheLock(String cacheKey) {if (StringUtils.isEmpty(cacheKey)) {return true;}int waitTimeOut = lockWaitTimeOut;try {while (waitTimeOut > 0) {//如果put成功说明加锁成功if (this.setnxWithExpire(cacheKey, cacheKey)) {return true;}waitTimeOut -= retryTime;//如果 tair 挂了这里会异常,快速失败String value = this.getLockValue(cacheKey);if (value == null) {continue;}sleep(retryTime);}//程序走到这里说明锁等待一定的时间,所以这里释放这个锁delCacheLock(cacheKey);} catch (Exception ex) {logger.error("getCacheLock exception key:{}", cacheKey, ex);}return true;}//释放锁public boolean delCacheLock(String cacheKey) {try {ResultCode result = tairWrapper.delete(cacheKey);return ResultCode.SUCCESS.equals(result);} catch (Exception ex) {logger.error("delete cacheKey exception key:{}", cacheKey, ex);}return false;}private void sleep(long millis) {try {Thread.sleep(millis);} catch (Exception ex) {logger.error("sleep exception", ex);}}private String getLockValue(String lockKey) {Result<DataEntry> getResult = tairWrapper.get(lockKey);if (getResult != null && ResultCode.SUCCESS.equals(getResult.getRc())) {Object obj = getResult.getValue().getValue();return obj == null ? null : obj.toString();}return null;}private boolean setnxWithExpire(String cacheKey, String value) {ResultCode resultCode = tairWrapper.put(cacheKey, value, INIT_VERSION, LOCK_EXPIRE_TIME);return ResultCode.SUCCESS.equals(resultCode);}}
这篇关于对锁的认识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!