我最近一直在阅读Java Concurrency in Practice - 很棒的书.如果你认为你知道并发是如何工作的,但是大部分时间你都面对真正的问题,感觉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; …Run Code Online (Sandbox Code Playgroud) 说真的,你如何处理所有这些例外而不必坚持?我是否读过太多关于异常处理的文章或者什么?我尝试过几次重构,每次我最终都会遇到更糟糕的事情.也许我应该承认异常确实发生了,只是喜欢编码只是快乐的道路?;)那么这段代码有什么问题(除了我懒得扔掉Exception而不是更具体的东西)?无论如何,不要轻易对我说.
public void Export(Database dstDb)
{
try
{
using (DbConnection connection = dstDb.CreateConnection())
{
connection.Open();
DbTransaction transaction = connection.BeginTransaction();
try
{
// Export all data here (insert into dstDb)
transaction.Commit();
}
catch (SqlException sqlex)
{
ExceptionHelper.LogException(sqlex);
try
{
transaction.Rollback();
}
catch (Exception rollbackEx)
{
logger.Error("An exception of type " + rollbackEx.GetType() +
" was encountered while attempting to roll back the transaction.");
}
throw new Exception("Error exporting message " + Type + " #" …Run Code Online (Sandbox Code Playgroud) 我有一个关于代码重复和重构的问题,希望它不是太笼统.假设您有一小段代码(~5行),这是一系列函数调用- 不是很低级别.这个代码在几个地方重复,因此在这里提取方法可能是个好主意.然而,在这个特定的例子中,这种新功能将遭受低内聚(其中,其中,通过难以找到该功能的良好名称而表现出来).原因可能是因为这个重复的代码只是更大算法的一部分 - 并且很难将其划分为命名良好的步骤.
在这种情况下你会建议什么?
编辑:
我想将问题保持在一般水平,以便更多人可能发现它有用,但显然最好用一些代码示例来支持它.这个例子可能不是有史以来最好的(它有很多种方式闻名),但我希望它能完成它的工作:
class SocketAction {
private static class AlwaysCreateSessionLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
Server.checkAllowedDeviceCount(socketAction._sess.getDeviceID());
socketAction.registerSession();
socketAction._sess.runApplication();
}
}
private static class AutoConnectAnyDeviceLoginHandler extends LoginHandler {
@Override
protected void onLoginCorrect(SocketAction socketAction) throws IllegalAccessException, IOException {
if (Server.isUserRegistered(socketAction._sess.getUserLogin())) {
Log.logSysInfo("Session autoconnect - acquiring list of action threads...");
String[] sa = Server.getSessionList(socketAction._sess.getUserID());
Log.logSysInfo("Session autoconnect - list of action threads acquired.");
for (int i = 0; i < sa.length; …Run Code Online (Sandbox Code Playgroud) 我被分配到多线程java服务器的一些性能和随机崩溃问题.尽管线程和线程安全对我来说并不是真正的新主题,但我发现设计一个新的多线程应用程序可能比尝试调整一些遗留代码困难一半.我浏览了一些着名的书籍以寻找答案,但奇怪的是,只要我阅读并分析所提供的例子,一切看起来都很清楚.然而,第二个我看看我应该工作的代码,我不再确定任何事情!必须是太多的理论知识和一些现实世界的经验或其他东西.
无论如何,回到主题,因为我正在做一些在线研究,我遇到了这段代码.一直困扰着我的问题是:在没有同步的情况下从两个独立的线程调用套接字上的getInputStream()和getOutputStream()是否真的安全?或者我现在对整个线程安全问题有点过于偏执?猜猜这就像连续第5本书中的内容一样,告诉你有多少东西可能出现并发问题.
PS.对不起,如果这个问题有点冗长或者说'noobie',请放轻松 - 这是我在这里的第一篇文章.
编辑:为了清楚起见,我知道套接字在全双工模式下工作,并且可以安全地同时使用它们的输入和输出流.当您在主线程中获取这些引用然后使用这些引用初始化线程对象时,我似乎很好,但是在两个不同的线程中获取这些流也是安全的吗?
@rsp:
所以我检查了Sun的代码,然后PlainSocketImpl就这两种方法进行同步,就像你说的那样.Socket但是,没有.getInputStream()并且getOutputStream()几乎只是包装器SocketImpl,所以可能并发问题不会导致整个服务器爆炸.但是,有一些不幸的时机,似乎事情可能会出错(例如,当方法已经检查错误条件时,某些其他线程关闭套接字).
正如您所指出的,从代码结构的角度来看,为每个线程提供流引用而不是整个套接字是个好主意.我可能已经重构了我正在处理的代码,如果不是因为每个线程也使用套接字的close()方法(例如当套接字接收"shutdown"命令时).据我所知,这些线程的主要目的是将消息排队以便发送或处理,因此可能是单一责任原则违规,并且这些线程不能关闭套接字(与分离的调制解调器接口相比) ?但是如果我长时间分析代码,看起来设计通常是有缺陷的,整个过程需要重写.即使管理层愿意付出代价,认真重构遗留代码,没有任何单元测试以及处理难以调试的并发问题,也可能弊大于利.不是吗?