3

这不是作业,而是我在网上找到的一个面试问题。

java代码是:

public class SimpleWebServer{
  public static void handleRequest(Socket c)
  {
    //Process the request
  }

  public static void main(String[] args) throws IOException
  { 
    ServerSocket server=new ServerSocket(80);
    while(true)
    {
       final Socket connection=server.accept();
       Runnable task=new Runnable(){
          @Override
          public void run()
          {
            handleRequest(connection);
          }
       };
       new Thread(task).start();
    }
  }
}

问题是当有高并发时会有什么潜在的问题?我的分析是:

  1. 它没有使用同步关键字,因此可能会发生竞争条件。
  2. 它应该使用线程池,这样效率更高。
  3. 好像对于每一个传入的线程,该类总是会创建一个新的ServerSocket,高并发的时候会消耗很多空间吗?
4

2 回答 2

4

我看到的主要问题是您已经确定的问题。每个请求的线程模型本质上是有缺陷的(正如 Nginx 和 lighttpd 在 Apache 上的广泛采用所证明的那样)。迁移到 ExecutorService(可能由线程池支持)将是一个不错的选择。

通过从每个请求一个线程更改为向 ExecutorService 简单提交任务,您正在将此应用程序移向基于事件的模型。网上有很多材料宣扬基于事件的模型优于基于线程的模型的可伸缩性优点。

在方法上使用“同步”handleRequest是一种相当暴力的策略,并且根据方法的特定内容,更细粒度的锁定策略(或无锁逻辑)将是首选。您提到的 ServerSocket 创建仅针对应用程序发生一次,因此这并不是真正的可伸缩性问题。该方法确实为每个连接accept创建一个新实例,但这些都很便宜。Socket查看 JDK 6 源代码,这包括分配 7 个布尔值、一个 lockObject和检查一些内部状态 - 即,可能不会成为问题。

基本上,你在正确的轨道上!希望这可以帮助。

于 2013-05-15T00:27:18.517 回答
0

以下是潜在问题列表

1) 产生一个线程需要 CPU 周期,并且每个线程都有自己的数据结构,这些数据结构会消耗内存。当一个线程阻塞时,JVM保存其状态并选择另一个线程运行,并恢复所选线程的状态,称为上下文切换。随着线程数量的增加,线程消耗的资源也越来越多。引入线程池是为了通过限制线程数量和重用线程而不是产生新线程来解决这个问题。

2) 最令人沮丧和重要的问题是使这些线程以同步方式共享或访问变量(例如状态)。很容易使这些变量不一致。因此,一些程序员更喜欢使用单线程环境(例如NIO)而不是多线程环境。

3) 当你想追踪问题时,没有日志系统会使线程环境更难调试。

4) 当你想接受一个新的客户端时,你不需要每次都实例化 new serversocket(),因为它是一个共享的 Serversocket 实例。

于 2013-05-15T00:36:01.473 回答