class ApplicationContext{
private final NetworkObject networkObject = new networkObject();
public ApplicationContext(){
networkObject.setHost("host");
networkObject.setParams("param");
}
public searchObjects(ObjectType objType){
networkObject.doSearch(buildQuery(objType));
}
}
class NetworkObject{
private final SearchObject searchObject = new SearchObject();
public doSearch(SearchQuery searchQuery){
searchObject.search(searchQuery); //threadsafe, takes 15(s) to return
}
}
考虑一个运行Web应用程序的Web服务器,它只创建一个ApplicationContext实例(singleton)并使用相同的applicationInstance来调用searchObjects,例如。
ApplicationContext appInstance =
ApplicationContextFactory.Instance(); //singleton
每个对网页的新请求都说'search.jsp'会拨打电话
appInstance.searchObjects(objectType);
我向'search.jsp'页面发出了1000个请求。所有线程都使用相同的ApplicationContext实例,而searchObject.search()方法需要15秒才能返回。我的问题是当所有其他线程执行searchObject.search()函数或所有线程同时执行searchObject.search()时,所有其他线程等待轮到他们执行(15秒),为什么?
我希望我的问题非常明确吗?
更新 谢谢大家澄清我的怀疑。这是我的第二个问题,当我这样做时应该观察到性能的差异:
public synchronized doSearch(SearchQuery searchQuery){
searchObject.search(searchQuery); //threadsafe, takes 15(s) to return
}
或
public doSearch(SearchQuery searchQuery){
searchObject.search(searchQuery); //threadsafe, takes 15(s) to return
}
我认为使用没有synchronized关键字的'doSearch'功能应该可以提供更高的性能。但是,当我今天测试它时,结果却出现了另一种情况。当我使用synchronized关键字时,性能相似或有时更好。
任何人都可以解释这种行为。我应该如何调试这些案件。
此致
佩里
答案 0 :(得分:5)
嗯,你没有指定代码中的任何同步,所以没有任何其他证据我怀疑所有线程将同时运行。如果SearchObject.search
包含某些同步,那么这显然会限制并发性。
请注意,您的JSP容器可能正在使用线程池来处理1000个请求,而不是创建1000个线程。
编辑:至于为什么synchronized
可能更快:有时并发对吞吐量实际上没有帮助。上下文切换,磁盘瓶颈,缓存未命中等等都会产生这种影响。通常通常拥有比核心更多的运行线程并不是一个好主意。
对于一个真实的例子,假设你有一千个购物者都想从一个相当小的商店买东西。你会怎么做?将所有1000个同时放入商店,或者在任何时间将其保持在商店中相当小的数量,并在外面排队?
答案 1 :(得分:1)
明智的做法是认识到绩效是针对特定环境的。在这种情况下,它可能是您的笔记本电脑或测试服务器上的软件性能。在考虑优化代码之前检查类似于生产环境的性能是明智的,因为那里的瓶颈可能与开发机器上的瓶颈完全不同。
作为一个例子;当我在笔记本电脑上使用大型数据库测试我的软件时,我总是被硬盘IO绑定。但是在生产中,数据库服务器有足够的内存和快速的磁盘,因此优化我的IO软件是不明智的。
类似于线程;我的笔记本电脑中的处理器可以同时运行一个或两个进程。拥有8个线程并不会加快速度。然而,生产机器可以同时处理8个螺纹。
我认为比性能更重要的是语义。使用像synchronous这样的关键字不仅对编译器有用,而且对(下一个)开发人员也有帮助。
通过使用同步,您可以与ApplicationContext上的所有其他同步方法共享锁,也可以使用与searchObject无关的方法。 就个人而言,我非常怀疑你希望在一个名为ApplicationContext的对象上进行同步。
如果searchObject不是线程安全的,我可能会推荐一个锁定对象。这涉及到口味:
public void doSearch(SearchQuery searchQuery){
synchronized(searchObject) {// Only if searchObject is guaranteed to be null
searchObject.search(searchQuery); //threadsafe, takes 15(s) to return
}
}
或
public class ApplicationContext {
private SearchObject searchObject = null;
private final Object searchObjectLock = new Object();
public void doSearch(SearchQuery searchQuery){
synchronized(searchObjectLock) {
searchObject.search(searchQuery); //threadsafe, takes 15(s) to return
}
}
}
不要忘记锁定每次使用searchObject以防止线程故障。使用这种细粒度锁定机制,您至少可以将ApplicationContext保留给不需要与searchObject相关的功能的类。
在您的情况下,我不会使用任何同步,因为它不是必需的,并在确定瓶颈之前检查类似生产的硬件。
如果searchObject使用数据库,请确保数据库属性已编入索引并使用该索引。如果需要进行1000次全表扫描,无论如何都不会很快......
答案 2 :(得分:0)
在您的情况下,它们将同时执行。
如果您想阻止这种情况,则需要进行某种同步以防止这种情况发生(例如,将方法声明为同步或使用锁定)。
ETA:
如果将doSearch()方法声明为synchronized,则一次只能有一个线程调用它。其他线程将阻塞,直到第一个线程完成,等待的线程将一次“放入”一个。你可以想象,如果你有很多线程调用那个函数,这将会破坏你的性能。
答案 3 :(得分:0)
如果你没有同步,那么每个线程将同时运行而不是阻塞锁。
请注意
// threadsafe
(如评论所示)意味着它可以正常工作,多个线程访问它 - 不它会阻止线程。
答案 4 :(得分:0)
答案 5 :(得分:0)
如果SearchObject.search已同步,则为yes。否则,试试看吧。
答案 6 :(得分:0)
为什么需要15秒?如果它正在等待磁盘访问并且您只有一个磁盘,那么无论您拥有多少线程,您都受到磁盘搜索速度的限制。在这种情况下,更多线程可能会更慢。