Java 多大的线程安全性就是太多?

Java 多大的线程安全性就是太多?,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,我最近一直在实践中阅读Java并发性——这本好书。如果你认为你知道并发是如何工作的,但是大多数时候你面对的是真正的问题,感觉SWAG是你能做的最多的,那么这本书肯定会对这个话题有所启发。当您尝试在线程之间共享数据时,有多少事情会出问题,这有点可怕。我想这可能让我对线程安全有点疯狂。现在我担心的是,由于同步太多,我可能会遇到一些活动性问题。下面是一段代码来说明: private final Hashtable<String, AtomicInteger> userSessions

我最近一直在实践中阅读Java并发性——这本好书。如果你认为你知道并发是如何工作的,但是大多数时候你面对的是真正的问题,感觉SWAG是你能做的最多的,那么这本书肯定会对这个话题有所启发。当您尝试在线程之间共享数据时,有多少事情会出问题,这有点可怕。我想这可能让我对线程安全有点疯狂。现在我担心的是,由于同步太多,我可能会遇到一些活动性问题。下面是一段代码来说明:

   private final Hashtable<String, AtomicInteger> userSessions =
new Hashtable<String, AtomicInteger>();

   public void registerUser(String userLogin) {
       synchronized(userSessions) {
           AtomicInteger sessionCount = userSessions.get(userLogin);
           if (sessionCount != null) {
               sessionCount.incrementAndGet();
           } else {
               userSessions.put(userLogin, new AtomicInteger(1));
           }
       }
   }

   public void unregisterUser(String userLogin) {
       synchronized(userSessions) {
           AtomicInteger sessionCount = userSessions.get(userLogin);
           if (sessionCount != null) {
               sessionCount.decrementAndGet();
           }
       }
   }

   public boolean isUserRegistered(String userLogin) {
       synchronized(userSessions) {
           AtomicInteger sessionCount = userSessions.get(userLogin);
           if (sessionCount == null) {
               return false;
           }
           return sessionCount.intValue() > 0;
       }
   }
私有最终哈希表用户会话=
新哈希表();
公共无效注册表用户(字符串userLogin){
已同步(用户会话){
AtomicInteger sessionCount=userSessions.get(userLogin);
if(sessionCount!=null){
sessionCount.incrementAndGet();
}否则{
put(userLogin,新的原子整数(1));
}
}
}
public void unregisterUser(字符串userLogin){
已同步(用户会话){
AtomicInteger sessionCount=userSessions.get(userLogin);
if(sessionCount!=null){
sessionCount.decrementAndGet();
}
}
}
公共布尔值isUserRegistered(字符串userLogin){
已同步(用户会话){
AtomicInteger sessionCount=userSessions.get(userLogin);
if(sessionCount==null){
返回false;
}
返回sessionCount.intValue()>0;
}
}
我试着让它一切正常:在静态部分构造同步集合,并存储在静态最终引用中以安全发布,锁定集合(而不是
this
——这样我就不会阻止代码所在的整个类),并使用原子包装类作为原语。这本书提到,做得过多也可能会导致问题,但我似乎需要更多的时间来完全理解它您如何确保此代码线程安全,并确保它不会受到活动性和性能问题的影响?


编辑:将其转换为实例方法和变量,最初所有内容都声明为静态-糟糕,糟糕的设计。还将userSessions设置为私有(之前我将其公开)。

从您的代码中可以看出,您在
\u userSessions
上的同步应该足够了,因为您没有公开
AtomicInteger
对象


在这种情况下,
AtomicInteger
提供的附加安全性是不需要的,因此本质上您在这里将其用作可变的
整数。如果您担心
AtomicInteger
中的额外开销,您可以在映射中放置一个嵌套的静态类,其中包含会话计数作为唯一属性(或者更难看一点:将int[1]添加到映射中,只要它们不暴露在此类之外)

使用
ConcurrentHashMap
,以便可以使用
putIfAbsent
。您不需要
AtomicInteger
代码来同步

   public final ConcurrentMap<String, AtomicInteger> userSessions =
       new ConcurrentHashMap<String, AtomicInteger>();

   public void registerUser(String userLogin) {
       AtomicInteger newCount = new AtomicInteger(1);
       AtomicInteger oldCount = userSessions.putIfAbsent(userLogin, newCount);
       if (oldCount != null) {
           oldCount.incrementAndGet();
       }
   }

   public void unregisterUser(String userLogin) {
       AtomicInteger sessionCount = userSessions.get(userLogin);
       if (sessionCount != null) {
           sessionCount.decrementAndGet();
       }
   }

   public boolean isUserRegistered(String userLogin) {
       AtomicInteger sessionCount = userSessions.get(userLogin);
       return sessionCount != null && sessionCount.intValue() > 0;
   }
公共最终ConcurrentMap用户会话=
新的ConcurrentHashMap();
公共无效注册表用户(字符串userLogin){
AtomicInteger newCount=新的AtomicInteger(1);
AtomicInteger oldCount=userSessions.putIfAbsent(userLogin,newCount);
if(oldCount!=null){
oldCount.incrementAndGet();
}
}
public void unregisterUser(字符串userLogin){
AtomicInteger sessionCount=userSessions.get(userLogin);
if(sessionCount!=null){
sessionCount.decrementAndGet();
}
}
公共布尔值isUserRegistered(字符串userLogin){
AtomicInteger sessionCount=userSessions.get(userLogin);
返回sessionCount!=null&&sessionCount.intValue()>0;
}
注意,这个漏洞

尝试使用非泄漏版本:

   public final ConcurrentMap<String, Integer> userSessions =
       new ConcurrentHashMap<String, Integer>();

   public void registerUser(String userLogin) {
       for (;;) {
           Integer old = userSessions.get(userLogin);
           if (userSessions.replace(userLogin, old, old==null ? 1 : (old+1)) {
                break;
           }
       }
   }
   public void unregisterUser(String userLogin) {
       for (;;) {
           Integer old = userSessions.get(userLogin);
           if (old == null) {
               // Wasn't registered - nothing to do.
               break;
           } else if (old == 1) {
               // Last one - attempt removal.
               if (userSessions.remove(userLogin, old)) {
                   break;
               }
           } else {
               // Many - attempt decrement.
               if (userSessions.replace(userLogin, old, old-1) {
                   break;
               } 
           }
       }
   }
   public boolean isUserRegistered(String userLogin) {serLogin);
       return userSessions.containsKey(userLogin);
   }
公共最终ConcurrentMap用户会话=
新的ConcurrentHashMap();
公共无效注册表用户(字符串userLogin){
对于(;;){
整数old=userSessions.get(userLogin);
if(userSessions.replace(userLogin,old,old==null?1:(old+1)){
打破
}
}
}
public void unregisterUser(字符串userLogin){
对于(;;){
整数old=userSessions.get(userLogin);
if(old==null){
//没有注册-无事可做。
打破
}else if(old==1){
//最后一次尝试删除。
if(userSessions.remove(userLogin,old)){
打破
}
}否则{
//多次尝试减量。
if(userSessions.replace)(userLogin,old,old-1){
打破
} 
}
}
}
公共布尔值isUserRegistered(字符串userLogin){serLogin);
返回userSessions.containsKey(userLogin);
}

好书,我最近自己读过

在上面的代码中,我唯一要注意的是,同步块中不需要AtomicInteger,但我怀疑性能是否值得注意

跟踪性能的最佳方法是对其进行测试。围绕框架的关键区域设置自动集成负载测试并跟踪性能。如果负载测试包含足够宽的时间窗口和对工作流程的丰富使用,则负载测试也可能捕获您创建的任何死锁

虽然死锁似乎很容易避免,但它们可以很容易地以相当简单的工作流模式出现

类A锁定资源,然后调用B(可能像get/set一样简单),B也锁定资源。 另一个线程调用锁定资源的B,然后调用导致死锁的A

在使用富框架时,绘制工作流以查看类如何交互是很有用的。您可能能够发现此类问题。但是,对于真正的大型框架,它们可能会忽略不计。Th
private static final Map<String, Integer> _userSessions =
  new HashMap<String, Integer>();

private ReadWriteLock rwLock =
  new ReentrantReadWriteLock(false); //true for fair locks

public static void registerUser(String userLogin) {
  Lock write = rwLock.writeLock();
  write.lock();
  try {
       Integer sessionCount = _userSessions.get(userLogin);
       if (sessionCount != null) {
           sessionCount = Integer.valueOf(sessionCount.inValue()+1);
       } else {
           sessionCount = Integer.valueOf(1)
       }
       _userSessions.put(userLogin, sessionCount);
   } finally {
     write.unlock();
   }
}

public static void unregisterUser(String userLogin) {
  Lock write = rwLock.writeLock();
  write.lock();
  try {
       Integer sessionCount = _userSessions.get(userLogin);
       if (sessionCount != null) {
           sessionCount = Integer.valueOf(sessionCount.inValue()-1);
       } else {
           sessionCount = Integer.valueOf(0)
       }
       _userSessions.put(userLogin, sessionCount);
   } finally {
     write.unlock();
   }
}

public static boolean isUserRegistered(String userLogin) {
  boolean result;

  Lock read = rwLock.readLock();
  read.lock();
  try {
       Integer sessionCount = _userSessions.get(userLogin);
       if (sessionCount != null) {
           result = sessionCount.intValue()>0
       } else {
           result = false;
       }
   } finally {
     read.unlock();
   }

   return false;
}
public final ConcurrentMap<String, AtomicInteger> userSessions =
   new ConcurrentHashMap<String, AtomicInteger>();
//There are other concurrent Maps for different use cases

public void registerUser(String userLogin) {
  AtomicInteger count;
  if (!userSession.containsKey(userLogin)){
    AtomicInteger newCount = new AtomicInteger(0);
    count = userSessions.putIfAbsent(userLogin, newCount);
    if (count == null){
      count=newCount;
    }
    //We need ifAbsent here, because another thread could have added it in the meantime
  } else {
    count = userSessions.get(userLogin);
  }
  count.incrementAndGet();
}

public void unregisterUser(String userLogin) {
  AtomicInteger sessionCount = userSessions.get(userLogin);
  if (sessionCount != null) {
    sessionCount.decrementAndGet();
  }
}

public boolean isUserRegistered(String userLogin) {
  AtomicInteger sessionCount = userSessions.get(userLogin);
  return sessionCount != null && sessionCount.intValue() > 0;
}
private final ConcurrentMap<String, AtomicInteger> userSessions =
   new ConcurrentHashMap<String, AtomicInteger>();

public void registerUser(String userLogin) {
    AtomicInteger oldCount = userSessions.get(key);
    if(oldCount!=null && getAndIncrementIfNonZero(oldCount)>0) return;
    AtomicInteger newCount = new AtomicInteger(1);
    while(true) {
        oldCount = userSessions.putIfAbsent(key, newCount);
        if(oldCount==null) return;
        if(getAndIncrementIfNonZero(oldCount)>0) return;
    }
}

public void unregisterUser(String userLogin) {
   AtomicInteger sessionCount = userSessions.get(userLogin);
   if (sessionCount != null) {
       int endingCount = sessionCount.decrementAndGet();
       if(endingCount==0) userSessions.remove(userLogin);
   }
}

public boolean isUserRegistered(String userLogin) {
   AtomicInteger sessionCount = userSessions.get(userLogin);
   return sessionCount != null && sessionCount.intValue() > 0;
}

private static int getAndIncrementIfNonZero(AtomicInteger ai) {
    while(true) {
        int current = ai.get();
        if(current==0) return 0;
        if(ai.compareAndSet(current, current+1)) return current;
    }
}