之前我正在使用HashMap
public Map<SocketChannel, UserProfile> clients = new HashMap<SocketChannel, UserProfile>();
Run Code Online (Sandbox Code Playgroud)
现在我已经切换到ConcurrentHashMap以避免同步块,现在我遇到了问题,我的服务器每秒都有200-400个并发客户端,预计会随着时间的推移而增长.
现在看起来像这样
public ConcurrentHashMap<SocketChannel, UserProfile> clients = new ConcurrentHashMap<SocketChannel, UserProfile>();
Run Code Online (Sandbox Code Playgroud)
我的服务器设计就像这样.我有一个工作线程来处理大量的数据包.每个数据包都使用packetHandler子程序(不是线程的一部分)进行检查,几乎任何客户端都可以随时调用它,它几乎就像静态但不是.
我的整个服务器大多是单线程的,除了数据包处理部分.
无论如何,当有人使用命令时,如在线计算所有客户端并从中获取一些信息.
当计数正在进行时(这会导致我的问题),客户端也可能会断开连接并从ConcurrentHashMap中删除.
另外我想在这里添加一些代码.
int txtGirls=0;
int vidGirls=0;
int txtBoys=0;
int vidBoys=0;
Iterator i = clients.values().iterator();
while (i.hasNext()) {
UserProfile person = (UserProfile)i.next();
if(person != null) {
if(person.getChatType()) {
if(person.getGender().equals("m"))
vidBoys++;
else //<-- crash occurs here.
vidGirls++;
} else if(!person.getChatType()) {
if(person.getGender().equals("m"))
txtBoys++;
else
txtGirls++;
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的意思是我当然要通过在Iterator中添加一个try-catch Exception来跳过这些空客户端来修复它.
但我不明白,如果检查上面是否(人!= null)不应该嵌套的代码自动工作..
如果它不意味着它在迭代时被删除,这应该是不可能的,因为它是线程安全的wtf?
我该怎么办?或者是try-catch Exception最好的方法?
这是例外
java.lang.NullPointerException
at …
Run Code Online (Sandbox Code Playgroud) 我正在实现tbb的并发哈希映射,以将其性能与一组其他并发哈希表进行比较.
然而,我从中获得的性能是可怕的,我无法相信它与其他并发哈希表相比是那么慢
这是我的实现:
class TBB: public TestDs{
typedef tbb::concurrent_hash_map<int,int, HashCompare<int> > hash_t;
private:
hash_t _ds;
public:
TBB(const Configuration& config) : _ds(config.initial_count) {
}
bool containsKey(int key) {
hash_t::accessor a;
if(_ds.find(a,key)){
return true;
}
else
return false;
}
int get(int key) {
hash_t::accessor a;
if(_ds.find(a,key)){
return (int)(a->second);
}
else
return 0;
}
int put(int key, int value) {
return _ds.insert( std::make_pair(key, value) );
}
int remove(int key) {
return _ds.erase(key);
}
int size() {
return _ds.size();
}
const char* name() { …
Run Code Online (Sandbox Code Playgroud) 我正在使用具有ConcurrentHashMap形式的对象存储的现有代码.在地图内存储可变对象,由多个线程使用.没有两个线程尝试通过设计一次修改对象.我关注的是线程之间修改的可见性.
目前,对象的代码在"setter"上具有同步(由对象本身保护)."getters"上没有同步,成员也不易变形.对我而言,这意味着无法保证可见性.但是,当对象被修改时,它会被重新放回到地图中(该put()
方法再次被调用,相同的键).这是否意味着当另一个线程将对象拉出地图时,它会看到修改?
我已经在stackoverflow,JCIP和java.util.concurrent的包描述中对此进行了研究.我基本上把自己搞糊涂了......但是最后一根稻草让我问这个问题来自于包装描述,它说:
在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前.
关于我的问题,"动作"是否包括在重新放置()之前对地图中存储的对象的修改?如果所有这些确实导致跨线程的可见性,这是一种有效的方法吗?我对线程比较新,感谢你的评论.
编辑:
谢谢大家的回复!这是我在StackOverflow上的第一个问题,它对我非常有帮助.
我必须接受ptomli的回答,因为我认为它最清楚地解决了我的困惑.也就是说,在这种情况下,建立"先发生过"关系并不一定会影响修改可见性.关于我在文中描述的实际问题,我的"标题问题"构造得很差.ptomli的答案现在jives与我在读JCIP:'为了确保所有线程共享见可变变量的最先进的日期值,读取和写入线程必须在一个共同的锁同步’(第37页).将对象重新放回到映射中不会为修改插入对象的成员提供此公共锁.
我很欣赏所有改变的提示(不可变对象等),我全心全意地同意.但对于这种情况,正如我所提到的,由于仔细的线程处理,没有并发修改.一个线程修改一个对象,另一个线程稍后读取该对象(CHM是对象传送器).我认为CHM不足以确保后来执行的线程会在我提供的情况下看到第一个修改.但是,我想很多人都正确地回答了标题问题.
并发Hashmap可以解决在hashmap中看到的同步问题.因此,如果我们使用hashmap同步键工作,添加和删除会很快.如果mulitple线程检查concurrentHashMap大小,那么检查hashmap大小呢?我们还需要同步关键词:如下所示:
public static synchronized getSize(){
return aConcurrentHashmap.size();
}
Run Code Online (Sandbox Code Playgroud) 我正在创建一个具有以下特征的memoization缓存:
什么会有一个优越的性能,或在什么条件下一个解决方案优于另一个解决方案?
ThreadLocal HashMap:
class MyCache {
private static class LocalMyCache {
final Map<K,V> map = new HashMap<K,V>();
V get(K key) {
V val = map.get(key);
if (val == null) {
val = computeVal(key);
map.put(key, val);
}
return val;
}
}
private final ThreadLocal<LocalMyCache> localCaches = new ThreadLocal<LocalMyCache>() {
protected LocalMyCache initialValue() {
return new LocalMyCache();
}
};
public V get(K key) {
return localCaches.get().get(key);
}
}
Run Code Online (Sandbox Code Playgroud)
ConcurrentHashMap的: …
Java ConcurrentHashMap在内部维护分区.每个分区可以单独锁定.可能存在多个线程访问的所有密钥落入同一分区并且分区可能没有帮助的情况.进一步增加分区数应该可以提高并发性.
为什么Java将分区计数的默认值设置为16而不是非常高的值?地图中有大量分区的表现是什么?
在JDK 1.6中,Doug Lea final
在该next
领域之前使用.
static final class HashEntry<K,V> {
final K key;
final int hash;
volatile V value;
final HashEntry<K,V> next;
Run Code Online (Sandbox Code Playgroud)
而在JDK 1.7中,next
字段前面是volatile
.我还注意到在JDK 1.7中,该get
方法采用getObjectVolatile
读取value
字段的方法,该字段具有易失性加载语义.
我不知道Doug Lea之前使用过什么final
.如果正确性存在问题,那么如何volatile
在JDK 1.7(也是JDK 1.8)中替换它?
编辑:
具体来说,我的问题是我们可以final
用volatile
JDK 1.6的实现代替吗?
java concurrency hashmap concurrenthashmap java.util.concurrent
Java doc说,方法值()和entrySet()的返回值由映射支持.因此,对地图的更改将反映在集合中,反之亦然.我不希望这发生在我的静态副本上.从本质上讲,我希望在我的DS上完成大量并发操作.但在某些情况下,我想迭代其静态快照.我想迭代静态快照,因为我假设与正在同时更新的版本相比,迭代静态快照会更快.
Javadoc ConcurrentHashMap#computeIfAbsent
说
计算应该简短,并且不得尝试更新此映射的任何其他映射.
但是,从我看到的,使用remove()
和clear()
内部方法mappingFunction
工作正常.例如这个
Key element = elements.computeIfAbsent(key, e -> {
if (usages.size() == maxSize) {
elements.remove(oldest);
}
return loader.load(key);
});
Run Code Online (Sandbox Code Playgroud)
在内部使用remove()方法有什么不好的后果mappingFunction
?
我有以下代码,它是一个玩具代码,但可以重现该问题:
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
public class TestClass3 {
public static void main(String[] args) throws InterruptedException {
// Setup data that we will be playing with concurrently
List<String> keys = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j");
HashMap<String, List<Integer>> keyValueMap = new HashMap<>();
for (String key : keys) {
int[] randomInts = new Random().ints(10000, 0, 10000).toArray();
keyValueMap.put(key, stream(randomInts).boxed().collect(toList()));
}
// Entering danger zone, …
Run Code Online (Sandbox Code Playgroud) java ×9
concurrency ×4
hashmap ×2
c++ ×1
caching ×1
null ×1
performance ×1
tbb ×1
thread-local ×1
try-catch ×1