加锁的位置 (eq:map<key,map<>> 双集合 怎么 只加锁 在用到的对象位置,而不是把整个集合锁住)
比如上边的map里套map 定义变量为data,例如组队副本 npc 为1 下有众多房间 即Map<1,<roomId,room>> ,处于多线程下,一个线程在 npc为1的下边建一个房间,房间id为1即Map<1,<1,room>> ,这时候另外一个线程同时也要在 npc为1下建一个房间id为2的房间 即 Map<1,<2,room>> ,那我代码里肯定是 先
<roomId,room> rooms = data.get(1); 假如 rooms为null,则 rooms = new Map<roomId,room>(); 然后 put到 data里,即 data.put(1,rooms);但是问题就在这,多线程下, 怎么能保证 多个线程 只new 一个 rooms,而不是每个线程 都 各自new 一个rooms,这就是要加锁了,但是要想保证只new 一个rooms出来,那必须 要在整个data上加锁,这问题就来了,我明明就只想保证new 一个rooms出来,结果我把整个data锁起来,假如这边npc 的id 不仅仅有1,还有其他2,3,4,5,6,7... 这时候怎么办,如果所整个data,那其他npc 对应的rooms 也将被锁住,性能消耗出现问题,那怎么办呢,好办,先把 data 定义成 ConcurrentHashMap,即
ConcurrentHashMap data = null; 然后利用 ConcurrentHashMap 中的 putIfAbsent()方法,ConcurrentHashMap为线程安全的集合,putIfAbsent()也为线程安全的原子级 操作,所以
可以 直接 data.putIfAbsent(1,new <roomId,room>); putIfAbsent()含义即 key对应的值 如果不存在的话就new一个出来然后放进去(第一次putIfAbsent()直接返回null,
同普通map 的put方法一样,如果map中没有key对应的值,第一次put时返回null,以后每次由于有值了返回才是 key对应的那个存在的值,这里putIfAbsent()第一次如果没有就new 值然后放进去 后
返回同理也是null,以后get才会拿到key对应的值),如果存在就返回旧的值(不会new,直接用旧的返回)。分析一下,假如2个线程都同时来npc为1的rooms,2个拿到的rooms假如都是null,都需要
new 新的rooms,第一个线程先putIfAbsent()时,由于 putIfAbsent() 方法是原子级的线程安全,使得第二个线程调用 putIfAbsent()时处于阻塞状态,直到第一个线程调用putIfAbsent()
结束后释放锁后第二个线程才能够调用 putIfAbsent(),由于第一个已经new一个新的而且放入到data里了,第二个线程调用putIfAbsent()时从data里能拿到值了所以就不会再new新的,而是直接就用
第一个线程 new的那个值,这样就保证了多线程建的同步安全。 补:还有一种恶心的同步加锁,比如 组队副本中 有Map<npcId,Map<roomId,room>> 这个结构定义变量为data, 这个 data 有加入房间(将玩家加入到room中)、有退出房间(room 中删除玩家,
room中没人时这个room还要从data里remove)、有 快速匹配房间 操作(将room从data中remove掉),这三种操作都会修改room,有的是给room中加人,有的是给room中减人和删除room,有的是
直接就删除room,这时候肯定就要加锁了,比如一个线程在我给room加人时,另外一个线程刚好把这个room给从data中remove 掉了,那就算第一个我的线程 给room中加人成功了又有什么用,你加成功了
结果这个房间都从data中remove掉了,也就是你是在第一个线程加到room里,结果第二个线程把room 从data中remove掉,那第一个线程加的那个人就进入二次元空间了,因为那个room在data中的引用被
remove了,room整个是被遗弃了,room整个被删除进入回收状态,room整个进入二次元了,那room里的人肯定更是进入二次元空间了,加入这时候在加人时 有 人每天加入房间次数的限制,那第一个线程
处理的那个人就会被坑掉一次,这样数据就错乱了,所以就要加锁,那这个锁又怎么加呢? Map<roomId,room> rooms = data.get(npcId); 难道对这个rooms进行加锁? 那假如我在加入一个房间,
结果把整个npc下对应的rooms都锁住了,其他玩家的一些不是 加人都房间的操作(比如 退出房间等操作)也是不能做了,那不加整个rooms的锁的话又怎么能不会造成数据错乱呢? 解决方法有一个思路,
就是操作哪个共享的对象就对哪个对象加锁,这里加人是操作单个room,那就对整个room加锁才是最合法的做法,那又怎么保证同步数据安全呢?解决方法是在 room中加一个状态,即我加锁在这单个room上,
然后在同步块中 进行这个单个room 中那个状态的判断,由于其他线程操作时也会锁住这个单个room,所以这个状态的修改肯定是只能由一个线程同时修改,所以就不会数据错乱问题了。
加锁的位置 (eq:map<key,map<>> 双集合 怎么 只加锁 在用到的对象位置,而不是把整个集合锁住)的更多相关文章
- map泛型 map不指定泛型 与 Map<Object,Object>的区别
map泛型 map不指定泛型 与 Map<Object,Object>的区别 private void viewDetail(){ Map map1 = new HashMap(); Ma ...
- InputSplit—>RecordReder—>map(key,value,context)的过程解析
上图首先描述了在TaskTracker端Task(MapTask.ReduceTask)的执行过程,MapTask(org.apache.hadoop.mapred)首先被TaskRunner调用,然 ...
- Java之Map接口(双列集合)
Map集合概述 现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射.Java提供了专门的集合类用来存放这种对象关系的对 ...
- Map<Key,Value>基于Value值排序
Map<Key,Value> 排序默认是按照KEY值的升序来进行. 针对按照Value来进行排序有两种方法: 第一种 使用TreeMap 代码如下 public class test{ ...
- js解析json,js转换json成map,获取map的key,value
json串格式 { "10.10.11.1": { "target_1": "34.2", "target_3": &q ...
- C++ map<key , value> key值为指针
STL中map的key能否用char *呢?当然可以! 在程序中需要用到一个map,本来是这样写的, map<string, int> mapStr; 为了追求效率,把string改成了c ...
- map访问key不存在的情况下,用find。比[]直接访问的意思不一样,map[key]不返null
key不存在的话则创建一个pair并调用默认构造函数 map<CGuid, CLibItem>::iterator iterItem = m_world->m_library_sce ...
- Sass Maps的函数-map-remove($map,$key)、keywords($args)
map-remove($map,$key) map-remove($map,$key) 函数是用来删除当前 $map 中的某一个 $key,从而得到一个新的 map.其返回的值还是一个 map.他并不 ...
- Sass函数:Sass Maps的函数-map-has-key($map,$key)
map-has-key($map,$key) 函数将返回一个布尔值.当 $map 中有这个 $key,则函数返回 true,否则返回 false. 前面的示例,当 $key 不在 $map 中时,使用 ...
随机推荐
- 死磕Lambda表达式(四):常用的函数式接口
失去人性,失去很多:失去兽性,失去一切.--<三体> 在Java8支持Lambda表达式以后,为了满足Lambda表达式的一些典型使用场景,JDK为我们提供了大量常用的函数式接口.它们主要 ...
- requests模块使用一
1.安装与简介 Urllib和requests模块是python中发起http请求最常见的模块,但是requests模块使用更加方便简单. pip install requests 2.GET请求 2 ...
- SpringMVC框架——转发与重定向
网上摘取一段大神总结的转发与重定向的区别,如下: 转发(服务端行为) 形式:request.getRequestDispatcher().forward(request,response) 转发在服务 ...
- C++中 string 中的方法的使用详解
string 字符串在所有的语言中都非常重要,c++也不例外,接下来我们将介绍string中的常用方法 1. size() 和 length() 函数 : 他们返回字符串的真实长度,且不会因为空格而截 ...
- JS中iframe子页面与父页面之间通信
iframe子页面与父页面通信根据iframe中src属性是同域链接还是跨域链接,通信方式也不同. 一.同域下父子页面的通信 父页面parent.html <html> <head& ...
- 使用Eclipse开发python
第一步:下载python插件http://sourceforge.net/projects/pydev/files/pydev/PyDev%204.1.0/第二步:在Eclipse上安装插件a.假设E ...
- 基于Modbus三种CRC16校验方法的性能对比
目录 1.背景介绍 2. CRC校验的三种方法 2.1. 直接计算CRC校验 2.2. 查短表法计算CRC16校验 2.3.查大表法计算CRC16校验 3.三种校验方式的测试方法 3.1.直接计算CR ...
- SpringMVC常见面试题总结(超详细回答)
SpringMVC常见面试题总结(超详细回答) 1.什么是Spring MVC ?简单介绍下你对springMVC的理解? Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的 ...
- Oracle 11g服务端的安装和配置
1.双击Oracle11g_database安装目录下的Setup.exe. 2.选择“基本安装”,设置“安装位置”,填写“数据库名”和“口令”,点击“下一步”. 3.点击“下一步”. 4.一般会出现 ...
- ESPCMS-Seay自动加手工代码审计
ESPcms代码审计 源码下载地址:http://yesky.91speed.org.cn/sw/180001_190000/rar/espcms_utf8_5.4.12.05.14.rar 1.自动 ...