Java多线程似乎没有正确工作

时间:2013-10-02 18:22:37

标签: java multithreading

我有一个处理某事的类。我正试图并行运行这个类的一些实例。

但是,我不确定在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();
         }
     }
}

3 个答案:

答案 0 :(得分:4)

的确,你并非“做得对”。如果要创建多线程Java应用程序,那么首先要使用java.util.concurrent包。

从您的代码中可以看出,您希望并行运行十个任务。我假设在“数字运算和处理”之后,你需要聚合结果并在主线程中对它们做一些事情。为此,invokeAll()的{​​{1}}方法效果很好。

首先,实施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()

答案 1 :(得分:3)

  

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

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

我不会像这样旋转,而是使用wait / notify:

// 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();
    }
}

请注意,您应该仔细处理线程中断。在运行循环时测试isInterrupted()并在抛出InterruptedException时重新中断线程总是一个很好的模式。

  

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

因此虽然线程大多是休眠状态,但由于你的Thread.sleep(1),它们仍然每秒循环1000次。如果你增加了休眠时间(使isRunning成为volatile之后),它们会循环较少,但正确的机制是使用wait / notify来通知线程。

答案 2 :(得分: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,并且因为if线程处于等待状态而中断线程。

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