2

我有一个处理某些东西的类。我正在尝试并行运行此类的多个实例。

但是,我不确定在TaskManager.startAll()调用r.go()时是否会导致 r 开始在自己的线程中或在主线程中运行?

我得到的总执行时间似乎非常高,尽管我尝试优化,但似乎没有任何效果。此外,如果我在 Netbeans 中对我的项目运行分析器,它会将所有线程显示为处于睡眠状态。所以我想知道我是否做错了什么?

这是类的结构:

public class TaskRunner implements Runnable {
   private boolean isRunning = false;
   public void run() {
        while(true) {
            while (! running) {
                try {
                    Thread.sleep(1);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            process();
        }
    }

    public void go() {
       isRunning = true;
    }

    public void stop() {
       isRunning = false;
    }

    private void process() {
       //Do some number crunching and processing here
    }
}

以下是它们的运行/管理方式:

public class TaskManager {
     private ArrayList<TaskRunner> runners = new ArrayList<>();
     public TaskManager() {
        for (int i = 0; i < 10; i++) {
            TaskRunner r = new TaskRunner();
            new Thread(r).start();
            runners.add(r);
        }
     }

     public void startAll() {
        for (TaskRunner r : runners) {
           r.go();
         }
     }
}
4

3 回答 3

4

事实上,你没有“做对了”。如果要创建多线程 Java 应用程序,则从java.util.concurrent包开始。

从您的代码中可以看出,您希望并行运行十个任务。我假设在“数字运算和处理”之后,您需要汇总结果并在主线程中对它们进行处理。为此, 的invokeAll()方法ExecutorService效果很好。

首先,实施Callable以完成您在process()方法中显示的工作。

final class YourTask implements Callable<YourResults> {

  private final YourInput input;

  YourTask(YourInput input) {
    this.input = input;
  }

  @Override
  public YourResults call() 
    throws Exception 
  {
    /* Do some number crunching and processing here. */
    return new YourResults(...);
  }

}

然后创建您的任务并运行它们。这将取代您的main()方法:

Collection<Callable<YourResults>> tasks = new List<>(inputs.size());
for (YourInput i : inputs) 
  tasks.add(new YourTask(i));
ExecutorService workers = Executors.newFixedThreadPool(10);
/* The next call blocks while the worker threads complete all tasks. */
List<Future<YourResult>> results = workers.invokeAll(tasks);
workers.shutdown();
for (Future<YourResult> f : results) {
  YourResult r = f.get();
  /* Do whatever it is you do with the results. */
  ...
}
于 2013-10-02T19:00:51.217 回答
3

但是,我不确定是否在 TaskManager.startAll() 中,当我调用 r.go() 时,这是否会导致 r 开始在自己的线程中运行,还是在主线程中运行?

所以我的第一条评论是你应该这样做isRunningvolatile因为它是在线程之间共享的。如果线程在变为 true 时没有启动(或者似乎延迟启动),那么我怀疑这是你的问题。 提供线程之间的内存同步,因此等待更改的线程将立即看到volatile调用go()并进行更改的线程。isRunning

我不会像这样旋转,而是使用等待/通知:

// this synchronizes on the instance of `TaskRunner`
synchronized (this) {
   // always do your wait in a while loop to protect against spurious wakeups
   while (!isRunning && !Thread.currentThread().isInterrupted()) {
      try {
          // wait until the notify is called on this object
          this.wait();
      } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          e.printStackTrace();
      }
 }

然后在该go()方法中,您应该执行以下操作。 stop()会是相似的。

public void go() {
    synchronized (this) {
       isRunning = true;
       this.notifyAll();
    }
}

请注意,您应该小心处理线程中断。在 while running 循环中测试并在抛出isInterrupted()时重新中断线程InterruptedException始终是一个好的模式。

我得到的总执行时间似乎非常高,尽管我尝试优化,但似乎没有任何效果。此外,如果我在 Netbeans 中对我的项目运行分析器,它会将所有线程显示为处于睡眠状态。

因此,尽管线程大多处于休眠状态,但由于您的Thread.sleep(1). 如果您增加睡眠时间(在制作isRunningbe之后volatile),它们会循环更少,但正确的机制是使用等待/通知来向线程发出信号。

于 2013-10-02T18:37:50.463 回答
2

糟糕的解决方案,可怕的。首先,我强烈建议您开始阅读一些类似[this]的教程 其次,如果线程应该等待一个信号来完成某项工作,那么您为什么不等待它们!!!!!!,类似这样的东西

import java.util.ArrayList;
public class TaskManager
{
     //////////////////////
     public volatile static Signal wait=new Signal();
     ////////////////////// 
     private ArrayList<TaskRunner> runners = new ArrayList<>();
     public TaskManager()
     {
        for (int i = 0; i < 10; i++)
        {
            TaskRunner r = new TaskRunner();
            new Thread(r).start();
            runners.add(r);
        }
        try {
          Thread.sleep(1000);
        startAll();
          Thread.sleep(1000);
        pauseAll();
          Thread.sleep(1000);
        startAll();
        Thread.sleep(1000);
        haltAll();System.out.println("DONE!");
        }catch(Exception ex){}
     }

     public void startAll()
     {
         synchronized(wait){
                wait.setRun(true);;
                wait.notifyAll();
         }
     }
     public void pauseAll(){
     wait.setRun(false);
     }
     public void haltAll(){
      for(TaskRunner tx:runners){tx.halt();}
      }
     public static void main(String[] args) {
      new TaskManager();
    }
}

class TaskRunner implements Runnable
{
   private Thread thisThread;
   private volatile boolean run=true;
   public void run()
    {
        thisThread=Thread.currentThread();
        while(run){
        if(!TaskManager.wait.isRun()){
        synchronized(TaskManager.wait)
        {
             if(!TaskManager.wait.isRun()){
               System.out.println("Wait!...");
                try
                {
                  TaskManager.wait.wait();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                    break;
                }
             }
        }}
         process();
     }
    }
    private double r=Math.random();
    private void process(){System.out.println(r);try {
      Thread.sleep(10);
    } catch (Exception e) {
      // TODO: handle exception
    }}
    public void halt(){run=false;thisThread.interrupt();}
}
class Signal{
  private boolean run=false;

  public boolean isRun() {
    return run;
  }

  public void setRun(boolean run) {
    this.run = run;
  }

}

在上面的示例中,所有跑步者都在工作,直到 Signal run boolean 为 true,并且简单的 TaskManager 类在每次需要暂停线程时将 tit 设置为 false。关于停止,它只是将shutdown(run)标志设置为false,并且还因为如果线程处于等待状态而中断线程。

我希望我能证明你的解决方案就像梦想中的故事,也可以对我的解决方案进行足够的解释。有一个很好的并行应用程序:)

于 2013-10-02T19:02:15.463 回答