3

这只是关于实际线程设计的一般问题。我专门在android上使用Java,但一般设计将是这个问题的更好焦点。

它足够简单,这是线程中更好的方法或方法中的线程。

例子,

假设我们有 3 个方法/功能/随便什么。

public void readMail()
{
    //Logic...
}
public void postQuestion()
{
    //Logic...
}
public void answerQuestion()
{
    //Logic...
}

是不是更好


A:方法中的线程

public void readMail()
{
    new Thread(new Runnable()
    {
        public void run()
        {
            //Logic
        }
    }).start();
}

然后像在任何 OO 情况下一样调用您的方法。说

Email.readMail();

B:线程内的方法

//note this could be inside a method or a class that extends runnable
new Thread(new Runnable()
{
    public void run()
    {
        readMail();
        postQuestion();
        answerQuestion();
    }
}).start();
4

5 回答 5

4

第二个选项更适合被重写以使用Executors 等,所以我更喜欢那个版本。

于 2012-04-07T17:05:07.790 回答
4

线程内的方法

  • [+]如果您的方法不需要确保并发执行的属性,或者它们具有确定性的运行时行为(时间和性能),那么这种方法可以对应用程序的并发进行高级管理;即并发性保持在对象级别而不是方法级别。
  • [-]由于并发性保持在线程/对象级别,应用程序可能会失去响应性的概念。一个用户可能正在“发布问题”,而另一个用户正在“获取答案”;两者可以同时处理。

带方法的线程

  • [+]更细粒度的并发控制:每个方法都成为操作系统级别的一个执行单元。这就是为什么正如@LouisWasserman 所提到的,也许利用Executor框架会更有意义。
  • [-]通常线程资源丰富昂贵;所以这意味着当您在高频/负载应用程序中使用对一种方法进行多次调用时,您将遇到性能问题。特别是,如果存在方法间数据/逻辑依赖关系。在这方面,同步也成为一个问题,这就是为什么使用Actor模型可以提供更多帮助的原因。

我建议阅读更多关于Actor 模型及其可用实现的信息。

于 2012-04-07T17:34:18.040 回答
1

我更喜欢:


C:一个线程一个对象

public class Test {
  public static class MailReader implements Runnable {
    public void readMail() {
      //Logic...
    }
    @Override
    public void run() {
      while (!Thread.currentThread().isInterrupted()) {
        readMail();
      }
    }
  }

  public static class QuestionPoster implements Runnable {
    public void postQuestion() {
      //Logic...
    }
    @Override
    public void run() {
      while (!Thread.currentThread().isInterrupted()) {
        postQuestion();
      }
    }
  }

  public static class QuestionAnswerer implements Runnable {
    public void answerQuestion() {
      //Logic...
    }
    @Override
    public void run() {
      while (!Thread.currentThread().isInterrupted()) {
        answerQuestion();
      }
    }
  }
  public static void main(String[] args) throws FileNotFoundException {
    new Thread(new QuestionAnswerer()).start();
    new Thread(new QuestionPoster()).start();
    new Thread(new MailReader()).start();
  }
}

这允许所有的可能性,而无需任何额外的 grok 努力。如果您希望回复的邮件多于发布的问题,请制作更多MailReader的 s。

如果你看到

for ( int i = 0; i < 10; i++ ) {
  new Thread(new MailReader()).start();
}

确切地知道打算做什么,并且您知道这将起作用。

于 2012-04-08T15:39:11.033 回答
0

在第一个设计 (A) 中,每个方法实际上都是一个单独的线程,而在第二个设计 (B) 中,您只有一个线程。
这在很大程度上取决于您的应用程序逻辑和每个方法执行的操作:
如果您需要并行运行您的方法,那么 A 是正确的答案,但如果您需要在一个线程中顺序执行所有方法,那么 B 将是您的选择。

于 2012-04-07T17:13:53.667 回答
0

如果您正在构建一个实用程序供其他程序员使用,请注意客户端程序员可能根本不关心线程,可能只想编写一个单线程程序。除非有很好的理由这样做,否则您不应该强迫他们将线程问题拖到一个程序中,否则该程序可以在单线程中正常工作。这是否意味着您的库不能在内部使用线程?不!但是对于调用者来说,你的方法应该单线程的(除了它们的返回速度比没有线程实现时更快)。

你怎么能做到这一点?当有人调用你的一个方法时,阻塞调用线程,并将任务传递给一个工作线程池,后者可以并行执行它。工作线程完成任务后,解除对调用线程的阻塞,让它返回一个值给调用者。

通过这种方式,您可以获得并行性的性能优势,而无需强制调用者处理线程问题。

现在,另一方面,即使您决定您的库不需要在内部使用线程,您仍然应该使其成为线程安全的,因为客户端程序员可能想要使用线程。

换句话说,“方法中的线程”的决定没有理由吗?和“线程中的方法?” 需要耦合。如果这样做有性能优势,您可以使用“线程方法”,但这不应该影响调用者。(他们应该能够调用该方法并取回所需的返回值,而不必担心您是否在内部使用线程)。

如果您的模块是线程安全的,那么它也不会受到调用者是否使用线程的影响。所以如果客户端程序员想要使用线程,他们也可以使用“线程中的方法”。在某些情况下,您可能同时拥有“线程中的方法”和“方法中的线程”——您的模块可能在内部使用工作线程池 + 任务队列,并且您可能有多个调用者线程将任务推入队列并等待结果。

现在,虽然我说的好像您正在构建一个库,但实际上您可能只是构建供您自己使用的代码。但不管怎样,同样的原则也适用。如果你想使用线程来提高性能,最好将线程的使用封装在接口后面,这样程序的其余部分就不必知道或关心模块 XYZ 是否使用线程。同时,最好让每个模块都是线程安全的,这样调用者就可以决定是否使用线程。

于 2012-04-07T18:47:44.147 回答