代码之家  ›  专栏  ›  技术社区  ›  freddoo

为什么多线程似乎不能加速我的web应用程序?

  •  2
  • freddoo  · 技术社区  · 7 年前
    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
        }
    }
    

     ApplicationContext appInstance = 
                      ApplicationContextFactory.Instance(); //singleton
    

    每个对网页的新请求都会说“search.jsp”进行调用

     appInstance.searchObjects(objectType);
    

    我正在向“search.jsp”页面发出1000个请求。所有线程都使用相同的ApplicationContext实例,searchObject.search()方法需要15秒才能返回。我的问题是,当一个线程已经在执行searchObject.search()函数时,是否所有其他线程都会等待轮到它们执行(15秒),或者所有线程都会同时执行searchObject.search(),为什么??

    我希望我已经把我的问题说清楚了??

    谢谢大家澄清我的疑问。这是我的第二个问题,当我这样做时,应该观察到性能上的哪些差异:

    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关键字时,性能类似,有时甚至更好。

    有人能解释这种行为吗。我应该如何调试这种情况。

    当做

    佩里

    7 回复  |  直到 13 年前
        1
  •  5
  •   Jon Skeet    17 年前

    嗯,你没有 明确规定 SearchObject.search 但是包含一些同步,这显然会限制并发性。

    请注意,您的JSP容器可能使用线程池来服务1000个请求,而不是创建1000个线程。

    编辑:至于为什么使用 synchronized :有时并发实际上对吞吐量没有帮助。上下文切换、磁盘瓶颈、缓存未命中等都会产生这种影响。它是 通常 拥有比内核更多的运行线程不是一个好主意。

    举一个真实的例子,假设你有一千名购物者,他们都想从一家相当小的商店买东西。你会怎么做?在同一时间把所有1000人都放在商店里,或者在任何时候都把数量控制在商店里的一个相当小的数量,然后在外面排队?

        2
  •  1
  •   extraneon    17 年前

    例如,;当我在笔记本电脑上用一个大数据库测试我的软件时,我总是被硬盘IO绑定。然而,在生产中,数据库服务器有足够的内存和快速的磁盘,因此优化我的IO软件是不明智的。

    我认为比性能更重要的是语义。使用像synchronous这样的关键字不仅对编译器有指导意义,而且对(下一个)开发人员也有指导意义。

    通过使用synchronous,您可以与ApplicationContext上的所有其他同步方法共享锁,这些方法也可能与searchObject无关。

    如果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
          }
        }
    }
    

    在您的情况下,我不会使用任何同步,因为它不是必需的,并在确定瓶颈之前检查生产类硬件。

        3
  •  0
  •   Brian Agnew    17 年前

    如果没有同步,那么每个线程将并发运行,而不会阻塞锁。

    // threadsafe
    

    (如注释所示)意味着它将在多个线程访问它时正常工作- 它会阻塞线程。

        4
  •  0
  •   user54579    17 年前

        5
  •  0
  •   Apocalisp    17 年前

    如果SearchObject.search已同步,则为“是”。否则,就试试看吧。

        6
  •  0
  •   pjc50    17 年前

    为什么需要15秒?如果它正在等待磁盘访问,而您只有一个磁盘,那么无论您有多少线程,您都会受到磁盘搜索速度的限制。在这种情况下,更多的线程甚至可能更慢。

        7
  •  0
  •   Eric Petroelje    17 年前

    在您的情况下,它们都将同时执行。

    如果要防止这种情况发生,则需要某种同步来防止这种情况发生(例如,将方法声明为已同步或使用锁)。

    如果将doSearch()方法声明为已同步,则一次只能有一个线程能够调用它。其他线程将阻塞,直到第一个线程完成,等待的线程将一次一个地“让进来”。可以想象,如果有很多线程调用该函数,那么这将降低性能。