大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。
以下是正文!
首先上一串代码
public String buy(Long goodsId, Integer goodsNum) {
//查询商品库存
Goods goods = goodsMapper.selectById(goodsId);
//如果当前库存为0,提示商品已经卖光了
if (goods.getGoodsInventory() <= 0) {
return "商品已经卖光了!";
}
//如果当前购买数量大于库存,提示库存不足
if (goodsNum > goods.getGoodsInventory()) {
return "库存不足!";
}
//更新库存
goods.setGoodsInventory(goods.getGoodsInventory() - goodsNum);
goodsMapper.updateById(goods);
return "购买成功!";
}
我们看一下这串代码,逻辑用流程图表示如下:
从图上看,逻辑还是很清晰明了的,而且单测的话,也测试不出来什么bug。但是在秒杀场景下,问题可就大发了,100件商品可能卖出1000单,出现超卖问题,这下就真的需要杀个程序员祭天了。
正常情况下,如果请求是一个一个接着来的话,这串代码也不会有问题,如下图:
不同的时刻不同的请求,每次拿到的商品库存都是更新过之后的,逻辑是ok的。
那为啥会出现超卖问题呢?
首先我们给这串代码增加一个场景:商品秒杀(非秒杀场景难以复现超卖问题)。
秒杀场景的特点如下:
- 高并发处理:秒杀场景下,可能会有大量的购物者同时涌入系统,因此需要具备高并发处理能力,保证系统能够承受高并发访问,并提供快速的响应。
- 快速响应:秒杀场景下,由于时间限制和竞争激烈,需要系统能够快速响应购物者的请求,否则可能会导致购买失败,影响购物者的购物体验。
- 分布式系统: 秒杀场景下,单台服务器扛不住请求高峰,分布式系统可以提高系统的容错能力和抗压能力,非常适合秒杀场景。
在这种场景下,请求不可能是一个接一个这种,而是成千上万个请求同时打过来,那么就会出现多个请求在同一时刻查询库存,如下图:
如果在同一时刻查询商品库存表,那么得到的商品库存也肯定是相同的,判断的逻辑也是相同的。
举个例子,现在商品的库存是10件,请求1买6件,请求2买5件,由于两次请求查询到的库存都是10,肯定是可以卖的。
但是真实情况是5+6=11>10,明显有问题好吧!这两笔请求必然有一笔失败才是对的!
那么,这种问题怎么解决呢?
从上面例子来看,问题好像是由于我们每次拿到的库存都是一样的
,才导致库存超卖问题,那是不是只要保证每次拿到的库存都是最新
的话,这个问题不就迎刃而解了吗!
在说方案前,先把我的测试表结构贴出来:
CREATE TABLE `t_goods` (
`id` bigint NOT NULL COMMENT '物理主键',
`goods_name` varchar(64) DEFAULT NULL COMMENT '商品名称',
`goods_pic` varchar(255) DEFAULT NULL COMMENT '商品图片',
`goods_desc` varchar(255) DEFAULT NULL COMMENT '商品描述信息',
`goods_inventory` int DEFAULT NULL COMMENT '商品库存',
`goods_price` decimal(10,2) DEFAULT NULL COMMENT '商品价格',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
官方介绍:Redisson是一个基于Redis的Java驻留内存数据网格(In-Memory Data Grid)。它封装了Redis客户端API,并提供了一个分布式锁、分布式集合、分布式对象、分布式Map等常用的数据结构和服务。Redisson支持Java 6以上版本和Redis 2.6以上版本,并且采用编解码器和序列化器来支持任何对象类型。 Redisson还提供了一些高级功能,比如异步API和响应式流式API。它可以在分布式系统中被用来实现高可用性、高性能、高可扩展性的数据处理。
<!--使用redisson作为分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.16.8</version>
</dependency>
RedissonConfig.java
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
/**
* 所有对Redisson的使用都是通过RedissonClient对象
*
* @return
*/
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient() {
// 创建配置 指定redis地址及节点信息
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379").setPassword("123456");
// 根据config创建出RedissonClient实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
public String buyRedisLock(Long goodsId, Integer goodsNum) {
RLock lock = redissonClient.getLock("goods_buy");
try {
//加分布式锁
lock.lock();
//查询商品库存
Goods goods = goodsMapper.selectById(goodsId);
//如果当前库存为0,提示商品已经卖光了
if (goods.getGoodsInventory() <= 0) {
return "商品已经卖光了!";
}
//如果当前购买数量大于库存,提示库存不足
if (goodsNum > goods.getGoodsInventory()) {
return "库存不足!";
}
//更新库存
goods.setGoodsInventory(goods.getGoodsInventory() - goodsNum);
goodsMapper.updateById(goods);
return "购买成功!";
} catch (Exception e) {
log.error("秒杀失败");
} finally {
lock.unlock();
}
return "购买失败";
}
加上Redisson分布式锁之后,使得请求由异步变为同步,让购买操作一个一个进行,解决了库存超卖问题,但是会让用户等待的时间加长,影响了用户体验。
MySQL的行锁是一种针对行级别数据的锁,它可以锁定某个表中的某一行数据,以保证在锁定期间,其他事务无法修改该行数据,从而保证数据的一致性和完整性。
特点如下:
- MySQL的行锁只能在InnoDB存储引擎中使用。
- 行锁需要有索引才能实现,否则会自动锁定整张表。
- 可以通过使用“SELECT ... FOR UPDATE”和“SELECT ... LOCK IN SHARE MODE”语句来显式地使用行锁。
总之,行锁可以有效地保证数据的一致性和完整性,但是过多的行锁也会导致性能问题,因此在使用行锁时需要谨慎考虑,避免出现性能瓶颈。
那么回到库存超卖这个问题上来,我们可以在一开始查询商品库存的时候增加一个行锁,实现非常简单,也就是将
//查询商品库存
Goods goods = goodsMapper.selectById(goodsId);
原始查询SQL
SELECT *
FROM t_goods
WHERE id = #{goodsId}
改写为
SELECT *
FROM t_goods
WHERE id = #{goodsId} for update
那么被查询到的这行商品库存信息就会被锁住,其他请求想要读取这行数据时就需要等待当前请求结束了,这样就做到了每次查询库存都是最新的。不过同Redisson分布式锁一样,会让用户等待的时间加长,影响用户体验。
乐观锁机制类似java中的cas机制,在查询数据的时候不加锁,只有更新数据的时候才比对数据是否已经发生过改变,没有改变则执行更新操作,已经改变了则进行重试。
`version` int(11) DEFAULT NULL COMMENT '版本'
update t_goods
set goods_inventory = goods_inventory - #{goodsNum},
version = version + 1
where id = #{goodsId}
and version = #{version}
public String buyVersion(Long goodsId, Integer goodsNum) {
//查询商品库存(该语句使用了行锁)
Goods goods = goodsMapper.selectById(goodsId);
//如果当前库存为0,提示商品已经卖光了
if (goods.getGoodsInventory() <= 0) {
return "商品已经卖光了!";
}
if (goodsMapper.updateInventoryAndVersion(goodsId, goodsNum, goods.getVersion()) > 0) {
return "购买成功!";
}
return "库存不足!";
}
通过增加了版本号的控制,在扣减库存的时候在where条件进行版本号的比对。实现查询的是哪一条记录,那么就要求更新的是哪一条记录,在查询到更新的过程中版本号不能变动,否则更新失败。
前面的两种办法是通过每次都拿到最新的库存
从而解决超卖问题,那换一种思路:保证在扣除库存的时候,库存一定大于购买量
是不是也可以解决这个问题呢?
答案是可以的。回到上面的代码:
//更新库存
goods.setGoodsInventory(goods.getGoodsInventory() - goodsNum);
goodsMapper.updateById(goods);
我们把库存的扣减写在了代码中,这样肯定是不行的,因为在分布式系统中我们获取到的库存可能都是一样的,应该把库存的扣减逻辑放到SQL中,即:
update t_goods
set goods_inventory = goods_inventory - #{goodsNum}
where id = #{goodsId}
上面的SQL保证了每次获取的库存都是取数据库的库存,不过我们还需要加一个判断:保证库存大于购买量,即:
update t_goods
set goods_inventory = goods_inventory - #{goodsNum}
where id = #{goodsId}
AND (goods_inventory - #{goodsNum}) >= 0
那么上面那段Java代码也需修改一下:
public String buySqlUpdate(Long goodsId, Integer goodsNum) {
//查询商品库存(该语句使用了行锁)
Goods goods = goodsMapper.queryById(goodsId);
//如果当前库存为0,提示商品已经卖光了
if (goods.getGoodsInventory() <= 0) {
return "商品已经卖光了!";
}
//此处需要判断更新操作是否成功
if (goodsMapper.updateInventory(goodsId, goodsNum) > 0) {
return "购买成功!";
}
return "库存不足!";
}
还有一种办法和where条件一样,就是unsigned 非负字段限制,把库存字段设置为unsigned 非负字段类型,那么在扣减时也不会出现扣成负数的情况。
解决方案 | 优点 | 缺点 |
---|---|---|
redis分布式锁 | Redis分布式锁可以解决分布式场景下的锁问题,保证多个节点对同一资源的访问顺序和安全性,性能较高。 | 单点故障问题,如果Redis节点宕机,会导致锁失效。 |
MySQL的行锁 | 可以保证事务的隔离性,能够避免并发情况下的数据冲突问题。 | 性能较低,对数据库的性能影响较大,同时也存在死锁问题。 |
乐观锁 | 相对于悲观锁,乐观锁不会阻塞线程,性能较高。 | 需要额外的版本控制字段,且在高并发情况下容易出现并发冲突问题。 |
where条件和unsigned 非负字段限制 | 可以通过where条件和unsigned非负字段限制来保证库存不会超卖,简单易实现。 | 可能存在一定的安全隐患,如果某些操作没有正确限制,仍有可能导致库存超卖问题。同时,如果某些场景需要对库存进行多次更新操作,限制条件可能会导致操作失败,需要再次查询数据,对性能会产生影响。 |
方案有很多,用法结合实际业务来看,没有最优,只有更优。
全文至此结束,再会!
无论您远走何方请点击蓝字,想念Me 免费PPT模板经过几天的收集整理,PPT模板分享模块终于和老铁们见面了。本期是PPT模板分享的第一期,由于小编正在准备论文答辩,光为找到合适的PPT就花了不少时间,小编一气之下为老铁们找到了1000余篇PPT模板,本期为大家奉上论文答辩PPT模板1~20篇。专科、本科、XX科都适用哦!!!说明:由于每次分享20篇,不能一一为大家列举,小编任意选取5篇,每篇中取前5页做展示。展示第1篇<<滑动查看下一页PPT>>展示第2篇<<滑动查看下一页PPT>>展示第3篇<<滑动查看下一页PPT>>展示第4篇<<滑动查看下一页PPT>>展示第5篇<<滑动查看下一页PPT>>我要使用【名称】第1期论文答辩PPT模板1~20篇【提取码】2nwb【下载地址】https://pan.baidu.com/s/15W-uWMtWX0w3gNHLzufCZA【收费类型】免费下期预告看了本期分享,是否对老铁们有所帮助呢?你是不是想问:还有没有下期啊?答案是必须的
matplotlib三种代码风格importnumpyasnp importos importmatplotlib.pyplotasplt #notebook模式下 %matplotlibinline复制pyplotx=np.arange(0,10,1) y=np.random.randn(len(x)) plt.plot(x,y)#绘制以x为横坐标,y为纵坐标的折线图 plt.title('pyplot') plt.show()复制pylab#pylab不推荐使用 frompylabimport* x=arange(0,10,1) y=randn(len(x)) plot(x,y)#绘制以x为横坐标,y为纵坐标的折线图 title('pylab') show()复制ObjectOriented在matplotlib中,整个图像为一个Figure对象。在Figure对象中可以包含一个,或者多个Axes对象。每个Axes对象都是一个拥有自己坐标系统的绘图区域。其逻辑关系如下: 整个图像是fig对象。我们的绘图中只有一个坐标系区域,也就是ax。此外还
MongoDBManual(Version4.2)>Security>NetworkandConfigurationHardening配置强化网络强化要降低整个MongoDB系统的风险暴露,请确保只有可信主机才能访问MongoDB。MongoDB配置强化IP绑定从MongoDB3.6开始,MongoDB二进制文件,[mongod]和[mongos]默认绑定本地主机(localhost)。从MongoDB版本2.6到3.4,只有官方MongoDBRPM安装包(RedHat,CentOS,FedoraLinux和衍生产品)和DEB安装包(Debian,Ubuntu及衍生产品)中的二进制文件默认绑定到本地主机(localhost)。要了解有关此更改的更多信息,请参阅[本地主机绑定兼容性更改]。警告:在绑定到非本地主机(例如可公开访问的)IP地址之前,请确保已保护数据库集群防止未经授权的访问。有关安全建议的完整列表,请参阅[安全检查表]。至少需要要考虑[启用身份验证]和施[强化网络基础架构]。警告:确保只能在受信任的网络上访问[mongod]和[mongos]实例。如果您的系统具有
来源|CSDN资讯作者|屠敏出品|区块链大本营(blockchain_camp)受5G冲击最大的领域终将会是谁?提及当前科技圈有哪些热点词,那5G必是其中之一。6月6日,工业和信息化部正式向中国移动、中国联通、中国电信和中国广电发布了四张5G商用牌照,这意味着国内正式迈进5G时代。而作为全球唯一能够提供端到端5G商业解决方案的华为,也公开表示已为迎接中国的5G商用做好了准备。除此之外,国内手机厂商如小米、OPPO、一加等也迫不及待地于今年年初就带来了已具形态的5G手机展示,与此同时,BAT等科技公司也纷纷调整战略与组织架构布局,全面迎接全新的5G时代。在众多科技巨头与运营商抢滩5G市场之际,我们不禁也好奇5G究竟会将如何拥抱以及改变我们现有的技术生态?对此,有网友表示,5G取代了4G,可以极大地提升通信下载速度;也有人称,5G将在无人驾驶、智能家居、智慧城市大展身手,5G的诞生将改写物联网领域。为什么会这么说?5G又将如何改写?接下来,我们将一探究竟。5G的优势5G是新一代蜂窝移动通信技术,也是4G(LTE、WiMAX-A)、3G(UMTS)、2G(GSM)标准后的延伸。相比可打电话的
URI,URL,URN从上面的那幅图可以看出来,一共有三个不同的概念URI,URL,URN。这讨论这样的问题时,最好的方法就是回到原点啊,这里我们在RFC3986:UniformResourceIdentifier(URI):GenericSyntax里面收集了点资料:“AUniformResourceIdentifier(URI)是一个紧凑的字符串用来标示抽象或物理资源。”“AURI可以进一步被分为定位符、名字或两者都是.术语“UniformResourceLocator”(URL)是URI的子集,除了确定一个资源,还提供一种定位该资源的主要访问机制(如其网络“位置”)。“那我们无所不知的维基百科把这段消化的很好,并描述的更加形象了:“URI可以分为URL,URN或同时具备locators和names特性的一个东西。URN作用就好像一个人的名字,URL就像一个人的地址。换句话说:URN确定了东西的身份,URL提供了找到它的方式。”通过这些描述我们可以得到一些结论:首先,URL是URI的一种(通过那个图就看的出来吧)。所以有人跟你说URL不是URI,他就错了呗。但也不是所有的URI都是
Web.config中httpModules和httpHandlers的相关配置说明配置Modules和Handlers的时候,根据不同IIS的版本和应用程序池中不同的托管管道模式,在Web.config中也有不同的配置方式。1.托管管道模式为:集成为集成模式,配置在Web.config的configuration节点下的<system.webServer>节点下,如下所示<?xmlversion="1.0"?> <configuration> <system.web> <compilationdebug="true"targetFramework="4.0"/> </system.web> <system.webServer> <handlers> <addverb="*"name="d"path="*.asyn"type="Class1"/&g
内容优化(1)减少HTTP请求数:这条策略是最重要最有效的,因为一个完整的请求要经过DNS寻址,与服务器建立连接,发送数据,等待服务器响应,接收数据这样一个消耗时间成本和资源成本的复杂的过程。常见方法:合并多个CSS文件和js文件,利用CSSSprites整合图像,InlineImages(使用data:URLscheme在实际的页面嵌入图像数据),合理设置HTTP缓存等。 (2)减少DNS查找 (3)避免重定向 (4)使用Ajax缓存 (5)延迟加载组件,预加载组件 (6)减少DOM元素数量:页面中存在大量DOM元素,会导致javascript遍历DOM的效率变慢。 (7)最小化iframe的数量:iframes提供了一个简单的方式把一个网站的内容嵌入到另一个网站中。但其创建速度比其他包括JavaScript和CSS的DOM元素的创建慢了1-2个数量级。 (8)避免404:HTTP请求时间消耗是很大的,因此使用HTTP请求来获得一个没有用处的响应(例如404没有找到页面)是完全没有必要的,它只会降低用户体验而不会有一点好处。复制服务器优化(1)使用内容分发网络(CDN):把网站内容分
需要提前了解的知识点:JVM内存模型JVM垃圾回收算法下图是JVM内存区域划分的逻辑图JVM内存区域逻辑图从图中我们大概了解JVM相关的内存区域。JVM内存包括区域Heap(堆区)NewGeneration(新生代)EdenSurvivorFromSurvivorToOldGeneration(老年代)方法区PermanentGeneration(持久代)Stack(栈区)Metaspace(元空间)DirectByteBuffer(直接内存)下面我们就通过一些JVM启动参数来配置以上内存空间Heap(堆)内存大小设置-Xms512m设置JVM堆初始内存为512M -Xmx1g设置JVM堆最大可用内存为1G NewGeneration(新生代)内存大小设置-Xmn256m设置JVM的新生代内存大小(-Xmn是将NewSize与MaxNewSize设为一致。256m),同下面两个参数 -XX:NewSize=256m -XX:MaxNewSize=256m 还可以通过新生代和老年代内存的比值来设置新生代大小 -XX:NewRatio=3设置新生代(包括Eden和两个Survivor区)与
一、找程序员不用担心外遇程序员是对着电脑工作,周围同事大部分是男生。他的生活中基本接触不到mm,所以不会有办公室恋情的发生,也就不会有外遇问题发生。而且面对的诱惑少,不像销售啊等职位,需要和外人打交道,而且应酬多,所面对的诱惑多,外遇出轨问题容易发生。剪剪常常加班到半夜,我是非常放心的,唯一担心的就是他的身体是否吃得消。 二、程序员很老实在单位,老板让加班就加班。在家里,老婆说啥都听。mm们,有个老实的老公是不是很不错啊。他可能没你想象的那么浪漫,给你惊喜,但他会老老实实记住你说的每句话,按照你的要求去做。三、程序员很有耐心。这个理由我不多说了,没有耐心的话,怎么写代码。这个在mm当了妈妈会发现是个很好的优点的。我是个没有耐心的人,教小剪学东西他要是学不会,我会没有耐心发脾气的,但是剪剪很有耐心的,会陪他玩,慢慢教会小剪。 四、程序员很细心。写代码是个细活,不细心就会有bug,真写了bug,还得耐心细心的去找出来。在生活中,对于粗枝大叶的mm来说,有个细心的gg照顾不是很好嘛?反正我们家我是粗枝大叶型,剪剪是细心型。 五、程序员很热心。论坛上兄弟们有困难,他们会出手相助。生活中,朋友有
本文译自MattStauffer的系列文章.输入event:generate好在,我们的紧张等待终于结束了。在Laravel5中,你可以在EventServiceProvider中绑定(不存在的)事件和处理程序,只要执行phpartisanevent:generate,Artisan就会自动生成全部文件,包括事件和对应的处理程序。如何实现先看一下事件和处理程序的目录:app/ Events/ Event.php Handlers/ Events/复制1)打开app/providers/EventServiceProvider.php,找到$listen属性,通常情况下我们就在这里进行事件绑定,按照下面的格式添加一条绑定:protected$listen=[ DidSomethingEvent::class=>[ RespondOneWay::class, RespondAnotherWay::class ] ];复制2)执行phpartisanevent:generate3)搞定。检查一下:app/ Events/ Event.php DidSomethingEvent.php
大家好,又见面了,我是你们的朋友全栈君。快速排序(QuickSort)是对冒泡排序的一种改进,基本思想是选取一个记录作为枢轴,经过一趟排序,将整段序列分为两个部分,其中一部分的值都小于枢轴,另一部分都大于枢轴。然后继续对这两部分继续进行排序,从而使整个序列达到有序。递归实现:voidQuickSort(int*array,intleft,intright) { assert(array); if(left>=right)//表示已经完成一个组 { return; } intindex=PartSort(array,left,right);//枢轴的位置 QuickSort(array,left,index-1); QuickSort(array,index+1,right); }复制PartSort()函数是进行一次快排的算法。 对于快速排序的一次排序,有很多种算法,我这里列举三种。左右指针法选取一个关键字(key)作为枢轴,一般取整组记录的第一个数/最后一个,这里采用选取序列最后一个数为枢轴。设置两个变量left=0;right=N–1;从left一直向后走,
本文为您介绍如何使用攻击日志进行攻击日志索引、快速分析和查询。 背景信息Web应用防火墙默认提供攻击日志,详细记录攻击产生的时间、攻击源IP、攻击类型及攻击详情等信息,攻击日志仅支持查询或导出最近30天日志。攻击日志支持全文检索、模糊搜索和组合条件搜索等检索方式,同时支持根据检索条件下载日志,支持百万级日志下载。 操作指南 登录Web应用防火墙控制台,在左侧导航栏中,选择日志服务>攻击日志。 在日志查询页签,您可以根据需要,选择域名、攻击类型、执行动作及时间维度等信息,筛选查看攻击日志。字段说明: 域名过滤:支持多选,默认显示全部域名。 攻击类型过滤:支持多选,默认为全部攻击类型,攻击类型包括各个安全模块产生的观察和拦截日志。 执行动作过滤:单选,默认为全部,包括观察和拦截两种类型。 风险等级:单选,包括:高危、中危和低危三种类型。 时间范围:默认为近一小时,支持选择最近时间及相对时间,您根据需要进行选择。 自动刷新:自动刷新当前页面,默认为关闭,若开启开关,您可选择按照时间范围刷新当前页面,获取最新攻击日志数据。 设置完成检索条件,单击检索分析,即可查看检索分析结果。 在攻
一、黑白图像 当你需要让一张彩色的图片显示为黑白照片的时候,你可以用下面的一段代码。img.desaturate{ filter:grayscale(100%); -webkit-filter:grayscale(100%); -moz-filter:grayscale(100%); -ms-filter:grayscale(100%); -o-filter:grayscale(100%);}二、使用:not()在菜单上应用/取消应用边框先给每一个菜单项添加边框.navli{ border-right:1pxsolid#666;}然后再除去最后一个元素.navli:last-child{ border-right:none;}也可以直接
importjsonimporttimeimportrequestsimportpandasaspdmax_ids=[283033662846177,170058843019812,149030683477660,142433613341546,4777752970398295]headers={'referer':'https://weibo.com/2803301701/LwAYl0Nfk?refer_flag=1001030103_','user-agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/102.0.5005.63Safari/537.36Edg/102.0.1245.33','cookie':'SINAGLOBAL=5840860407191.644.1644806743109;SUB=_2A25PfbuqDeRhGeNH61oR9i3Iyz-IHXVsgcXirDV8PUJbkNAKLUz9kW1NSvWJkWxYTclvArPVGdYD02lTJ
题目大意: n个数,每个数小于\(2^k\),求满足下列条件的方案数。 题解:好蠢的题解。。。 #include<bits/stdc++.h> usingnamespacestd; constintN=2e5+9; constintmod=1e9+7; intt; intf[N]; intadd(intx,inty) { return(x%mod+y%mod)%mod; } intksm(intx,inty) { intres=1; while(y) { if(y&1) { res=1ll*res*x%mod; } x=1ll*x*x%mod; y>>=1; } returnres; } voidpre() { f[0]=1; for(inti=1;i<=2e5;i++)f[i]=f[i-1]*2%mod; } intmain() { pre(); scanf("%d",&t); while(t--) { intans=0; intn,k; scanf("%d%d
不知不觉这个系列已经写了六篇,园友的反响也很大,前五篇的评论数多达三百多,我的粉丝数在一周内增长了100多,证明还是有很多人对阅读感兴趣,也想提升自己。有园友叫我假.NET大神,哈哈哈...我也成大神了,假的也是大神,其实我连假大神都算不上,还是大家太抬举我了,哈哈哈... (备注:最近有些忙,没想到这么多人留下地址,我发过一些,有些人估计是发掉了,后面看到越来越多,就准备一起发,结果太忙,忘记发了。加上私信发多了被系统认为是广告,遭到屏蔽,在这里给出地址:链接:http://pan.baidu.com/s/1kVNpkZt密码:d79g。。过期了再更新,到时候提醒一下楼主。) 最近看到这个系列的支持度在逐渐减少,可能是这个系列本身不具备续写的潜力,所以本人将原本八篇的博文缩短至六篇。(最近有园友跟本人抱怨介绍了一些质量不高的书籍和分级不恰当的问题,在这里我向大家说明一下,这个系列不是介绍好书,而是只介绍本人所看过的书,看的书有好有坏,分类也是我个人的想法,虽然我尽量理性的区分类,但也难免有些地方出现错误,这个读者
转载请注明出处:http://www.cnblogs.com/fangkm/p/3405959.html Chromium项目采用Grit工具来打包生成程序需要的资源,如图片资源、字符串资源等,尤其是字符串资源,牵涉到国际化的问题。Chromium为需要的资源创建单独的项目工程,工程类型为实用工具,自定义工程的生成事件,在CustomBuild里调用grit命令,根据grd资源描述文件生成相关的资源。如chrome_strings工程生成国际化字符串资源、chrome_resources工程生成除字符串以外的资源,比如图片资源。 Grit工具接受grd资源描述文件,生成.h、.rc、.pak等文件。工具位于src\tools\grit文件夹下,采用python脚本编制。 src\tools\gritsettings下有个resource_ids文件,该文件定义Chromium工程中所有的grd文件所生成的资源编号开始区段,从而确保所有的资源ID不发生冲突。该文件格式描述如下: { "SRCDIR":"../..", "chrome/br
sed原理及使用 目录 前言 一、简介 二、处理流程 三、命令选项options 四、pattern 1.模式空间 2.模式空间的转换 3.地址匹配 五、procedure 1.替换命令:s 2.删除命令:d 3.插入行/追加行/替换行命令:i/a/c 4.打印命令:p/l/= 5.转换命令:y 6.取下一行命令:n 7. 读写文件命令:r/w 8.退出命令:q 六、小结 七、参考 回到顶部 前言 环境:centos6.5 sed版本:GNUsedversion4.2.1 本文的代码都是在这个环境下验证的。 回到顶部 一、简介 sed(StreamEditor)意为流编辑器,是Unix常见的命令行程序。是Bell实验室的LeeE.McMahon在1973年到1974年之间开发完成,目前可以在大多数操作系统中使用。 sed的出现是作为grep的一个继任者,因为grep只能简单的进行查找和替换,但是考虑还可能会有删除等各种需求,McMahon开发了一个更具通用性的工具。sed著名的语法规则包括使用/进行模式匹配,以及s///来进行替代。与同期存
在非注解的方式中,springmvc.xml文件中需要自己配置处理器映射器,处理器适配器,配置每一个控制器,并且每一个请求就要对应一个控制器类,开发很不方便。 注解方式的主要区别在于springmvc.xml文件的配置和处理器的开发代码。 1、springmvc.xml配置 <?xmlversion="1.0"encoding="UTF-8"?> <beansxmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframew