【问题标题】:Synchronization in threads for JavaJava的线程同步
【发布时间】:2009-09-10 20:24:36
【问题描述】:

我的应用中有一个自制的网络服务器。此 Web 服务器为进入套接字的每个请求生成一个新线程以被接受。我希望网络服务器等到它刚刚创建的线程中的特定点被命中。

我已经浏览了该站点上的许多帖子和网络上的示例,但是在我告诉线程等待后无法让网络服务器继续。一个基本的代码示例会很棒。

同步关键字是解决这个问题的正确方法吗?如果是这样,如何实现?我的应用程序的代码示例如下:

网络服务器

while (true) {
  //block here until a connection request is made
  socket = server_socket.accept();

  try {
    //create a new HTTPRequest object for every file request
    HttpRequest request = new HttpRequest(socket, this);

    //create a new thread for each request
    Thread thread = new Thread(request);

    //run the thread and have it return after complete
    thread.run();

    ///////////////////////////////
    wait here until notifed to proceed
    ///////////////////////////////
  } catch (Exception e) {
    e.printStackTrace(logFile);
  }
}

线程代码

public void run() {
  //code here

  //notify web server to continue here
}

更新 - 最终代码如下。每当我发送响应标头时,HttpRequest 只会调用resumeListener.resume()(当然,还将接口添加为单独的类,并在HttpRequest 中添加addResumeListener(ResumeListener r1) 方法):

网络服务器部分

// server infinite loop
while (true) {

  //block here until a connection request is made
  socket = server_socket.accept();

  try {
    final Object locker = new Object();

    //create a new HTTPRequest object for every file request
    HttpRequest request = new HttpRequest(socket, this);

    request.addResumeListener(new ResumeListener() {
      public void resume() {
        //get control of the lock and release the server
        synchronized(locker) {
          locker.notify();
        }
      }
    });

    synchronized(locker) {
      //create a new thread for each request
      Thread thread = new Thread(request);

      //run the thread and have it return after complete
      thread.start();

      //tell this thread to wait until HttpRequest releases
      //the server
      locker.wait();
    }
  } catch (Exception e) {
    e.printStackTrace(Session.logFile);
  }
}

【问题讨论】:

  • 作为一个学术问题,你为什么要编写自己的网络服务器代码?
  • 该应用程序显示加密内容,因此基本上通过创建自己的内部网络服务器,我可以执行身份验证。如果外部浏览器尝试使用我传递数据的同一个套接字,那么如果请求来自我的应用程序,它将没有我内部提供的正确凭据。
  • 我不明白这如何排除使用不同的网络服务器 - 几乎每个网络服务器都支持基于 IP 的身份验证,以及其他机制。
  • 我无法争辩它是如何写的。我只是用 Java 重写了一个基于 .NET 的应用程序,这就是它当前的编码方式,并且在当前状态下非常安全。
  • 问题是在编写网络服务器时很容易出错 - 例如,如果网络服务器对特别奇怪的请求的结束位置与沿途的某些代理有不同的想法,那么您可能会遇到有趣的安全问题。我强烈建议改用轻量级 servlet 容器...

标签: java multithreading synchronized


【解决方案1】:

您可以使用 java.util.concurrent.CountDownLatch 计数为 1。安排它的一个实例由父线程和子线程创建和共享(例如,在HttpRequest 的构造函数中创建它,并使其可由成员函数检索)。然后服务器在其上调用await(),当准备好释放其父线程时,该线程命中countDown()

【讨论】:

  • 感谢您的建议,我会尝试以这种方式实现。看起来很简单。
【解决方案2】:

您可能需要使用 Java Condition。来自文档:

条件(也称为条件 队列或条件变量)提供 一种让一个线程挂起的方法 执行(“等待”)直到收到通知 由某个状态的另一个线程 条件现在可能为真。

【讨论】:

  • 单独的条件变量有点难以打开代码;您还需要一个关联的 Lock 和一个标志来知道何时停止循环和阻塞。另外,无需直接使用 Condition 类;只需使用每个对象中隐含的监视器...
  • 另外,澄清一点 - 条件/锁定使系统更加明确,但独立于 synchronizedObject.wait() 使用的内置监视器。如果您需要将多个条件变量附加到单个锁上,它们会很有用,但在这种情况下,这有点矫枉过正。
  • 实际上Condition 是一个接口,我只是指出它是因为在实践中使用实现类是必要的。我链接到的页面有一个带有ReentrantLocks 的示例。
【解决方案3】:

首先,我赞同其他人的观点,即在这里重新发明轮子很可能会给您带来各种问题。但是,如果你想沿着这条路走下去,你想做的事情并不难。您是否尝试过 Jetty?

可能是这样的:

public class MyWebServer {

  public void foo() throws IOException {
    while (true) {
      //block here until a connection request is made
      ServerSocket socket = new ServerSocket();

      try {
        final Object locker = new Object();
        //create a new HTTPRequest object for every file request
        MyRequest request = new MyRequest(socket);
        request.addResumeListener(new ResumeListener() {
          public void resume() {
            locker.notify();
          }
        });
        synchronized(locker){

          //create a new thread for each request
          Thread thread = new Thread(request);

          //start() the thread - not run()
          thread.start();

          //this thread will block until the MyRequest run method calls resume
          locker.wait();
        }  
      } catch (Exception e) {
      }

    }
  }
}

public interface ResumeListener {
  public void resume();
}

public class MyRequest implements Runnable{
  private ResumeListener resumeListener;

  public MyRequest(ServerSocket socket) {
  }

  public void run() {
    // do something
    resumeListener.resume(); //notify server to continue accepting next request
  }

  public void addResumeListener(ResumeListener rl) {
    this.resumeListener = rl;
  }
}

【讨论】:

  • 感谢您的示例。这似乎很简单,我也会尝试一下。我了解实现自己的 Web 服务器的担忧我已经简要地研究了 Jetty,但没有继续下去。 Jetty 仍然是未来的可能性,但目前本土种植的表现良好。
【解决方案4】:

在调试器下运行并设置断点?

如果不可行,则从 System.in 中读取一行?

【讨论】:

    猜你喜欢
    • 2011-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多