Javamultithreading似乎没有正确工作
我有一个处理某事的课程。 我试图并行运行这个类的一些实例。
但是,我不确定在调用r.go()
时是否在TaskManager.startAll()
,是否会导致r开始在自己的线程中运行,或者在主线程内运行?
我得到的总执行时间似乎非常高,尽管我尝试优化,但似乎没有任何效果。 此外,如果我在Netbeans中的项目上运行一个分析器,它会显示所有线程都处于hibernate状态。 所以我想知道我做错了什么?
这是class级的结构:
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 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(); } } }
实际上,你并没有“做得对”。 如果要创建multithreadingJava应用程序,首先要使用java.util.concurrent
包。
从您的代码中可以看出,您希望并行运行十个任务。 我假设在“数字运算和处理”之后,你需要聚合结果并在主线程中对它们做一些事情。 为此, ExecutorService
的invokeAll()
方法运行良好。
首先,实现Callable
来完成您在process()
方法中显示的工作。
final class YourTask implements Callable { 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> 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> results = workers.invokeAll(tasks); workers.shutdown(); for (Future f : results) { YourResult r = f.get(); /* Do whatever it is you do with the results. */ ... }
但是,我不确定在调用r.go()时是否在TaskManager.startAll()中,是否会导致r开始在自己的线程中运行,或者在主线程内运行?
所以我的第一个评论是你应该使isRunning
变得volatile
因为它是在线程之间共享的。 如果线程在启动时没有启动(或者似乎在启动时延迟),那么我怀疑这是你的问题。 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(); } }
请注意,您应该仔细处理线程中断。 在while循环中测试isInterrupted()
并在抛出InterruptedException
时重新中断线程总是一个好的模式。
我得到的总执行时间似乎非常高,尽管我尝试优化,但似乎没有任何效果。 此外,如果我在Netbeans中的项目上运行一个分析器,它会显示所有线程都处于hibernate状态。
因此,虽然线程大多是hibernate状态,但由于Thread.sleep(1)
,它们仍然每秒循环1000次。 如果你增加了hibernate时间(在使isRunning
变为volatile
),它们会循环较少,但正确的机制是使用wait / notify来通知线程。
可怕的解决方案,太糟糕了。 首先我强烈建议你开始阅读一些像[this]这样的教程。其次,如果线程应该等待某个工作的信号,那么为什么你不等他们!!!!!,这样的事情
import java.util.ArrayList; public class TaskManager { ////////////////////// public volatile static Signal wait=new Signal(); ////////////////////// private ArrayList 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,并且还因为线程处于等待状态而中断线程。
我希望我可以certificate你的解决方案就像梦想故事一样,也可以解释我的解决方案。 有一个很好的并行应用:)