对锁的认识

2024-06-20 16:48
文章标签 认识 对锁

本文主要是介绍对锁的认识,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景

一、自己编写一个数据库连接池,涉及到共享连接对象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);}}

这篇关于对锁的认识的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1078699

相关文章

Java对异常的认识与异常的处理小结

《Java对异常的认识与异常的处理小结》Java程序在运行时可能出现的错误或非正常情况称为异常,下面给大家介绍Java对异常的认识与异常的处理,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参... 目录一、认识异常与异常类型。二、异常的处理三、总结 一、认识异常与异常类型。(1)简单定义-什么是

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

每天认识几个maven依赖(ActiveMQ+activemq-jaxb+activesoap+activespace+adarwin)

八、ActiveMQ 1、是什么? ActiveMQ 是一个开源的消息中间件(Message Broker),由 Apache 软件基金会开发和维护。它实现了 Java 消息服务(Java Message Service, JMS)规范,并支持多种消息传递协议,包括 AMQP、MQTT 和 OpenWire 等。 2、有什么用? 可靠性:ActiveMQ 提供了消息持久性和事务支持,确保消

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

SpringMVC-1.认识及配置

SpringMVC是一个基于请求驱动的Web框架,和structs一样是目前最优秀的基于MVC框架,现在的项目一般都使用SpringMVC代替Structs。 MVC模式中,Model是应用程序中用于处理应用程序数据逻辑的部分,通常模型对象在数据库中存取数据。View是应用程序中处理数据显示的部分,通常视图是依据模型数据创建。Controller是应用程序中处理用户交互的部分。通常控制器负责从视

【H2O2|全栈】关于HTML(1)认识HTML

HTML相关知识 目录 前言 准备工作 WEB前端是什么? HTML是什么? 如何运行HTML文件? 标签 概念 分类 双标签和单标签 行内标签和块标签 HTML文档结构 预告和回顾 UI设计相关 Markdown | Md文档相关  项目合作管理相关  后话 前言 本系列的博客将分享前端HTML的相关知识点。 本篇作为本系列的第一期博客,主要讲解H

认识鬼火引擎

一、Irrlicht简介 (1)概念Irrlicht引擎是一个用C++书写的高性能实时3D引擎,可以应用于C++程序或者.NET语言中。通过使用Direct3D(Windows平台)、OpenGL 或它自己的软件着色程序,可以实现该引的完​全跨平台。尽管是开源的,该Irrlicht库提供了可以在商业级的3D引擎上具有的艺术特性,例如动态的阴影,粒子系统,角色动画,室内和室外技术以及碰撞检测等。(

Jenkins--pipeline认识及与RF文件的结合应用

什么是pipeline? Pipeline,就是可运行在Jenkins上的工作流框架,将原本独立运行的单个或多个节点任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。 为什么要使用pipeline? 1.流程可视化显示 2.可自定义流程任务 3.所有步骤代码化实现 如何使用pipeline 首先需要安装pipeline插件: 流水线有声明式和脚本式的流水线语法 流水线结构介绍 Node:

git:认识git和基本操作(1)

目录 一、版本控制器 1.安装git 2.创建git本地仓库 3.配置git 二、git操作(1) 1.工作区、暂存区、版本库 2.添加文件 3.查看.git 4.修改文件 一、版本控制器         所谓的版本控制器,就是能让你了解到每一个文件的修改历史。相应的,在企业级开发中,用来记录一个工程的每一次改动和管理版本迭代,同时方便多人协作开发。         g

关于MANIFEST.MF的内容认识

文章来源 https://baike.baidu.com/item/MANIFEST.MF https://www.cnblogs.com/Gandy/p/7290069.html 一、百度百科关于MANIFEST.MF文件介绍 打开Java的JAR文件我们经常可以看到文件中包含着一个META-INF目录,这个目录下会有一些文件,其中必有一个MANIFEST.MF,这个文件描述了