2015-11-03 41 views
0

当用户输入TextField时:我需要通过WebRequest搜索一些数据并显示它们。Service.restart()生成大量不需要的线程数

当用户输入多个字符时,应取消上一次下载,并开始新的下载。

所以我使用一个任务下载数据和一个服务来显示数据,当任务返回它们时。

s = new Service(){ 
    @Override 
    protected Task createTask() { 
     return new Task<String>(){ 
      @Override 
      protected String call() throws Exception { 
       //DOWNLOAD DATA 
       System.out.println("1"); 
       Thread.sleep(1000); 
       System.out.println("2"); 
       Thread.sleep(1000); 
       System.out.println("3"); 
       Thread.sleep(1000); 
       return "banana"; 
      } 
     }; 
    } 
}; 
s.setOnSucceeded(new EventHandler<WorkerStateEvent>(){ 
    @Override 
    public void handle(WorkerStateEvent event) { 
     System.out.println(event.getSource().getValue() + " DISPLAYED"); 
    } 
}); 

//HANDLE KEY RELEASED ON A TEXTFIELD 
public void onTextFieldKeyReleased() { 
    s.restart(); 
} 

我注意到,每次服务重新启动时,都会出现另一个线程,直到出现一定数量的线程。 这使程序显示数据的时间很长。

当KeyReleasedEvent发生时,我希望服务执行的操作是取消当前正在运行的任务并开始一个新任务...每次新任务时都不会追加任务。

我该如何做到这一点?

+0

我无法重现“巨大的延迟“与您发布的代码。你可以创建一个[MCVE]吗? –

+0

@James_D也许你不能重现延迟,但你可以看到有很多线程被创建,我不希望这样的情况发生。 另外,我注意到在打印每个线程之前,生成的服务必须关闭,这需要一秒钟,对我来说太多了。 – user2468425

+0

我不明白为什么创建新线程会导致问题,但是您是否阅读了[documentation](http://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Service.html #setExecutor-java.util.concurrent.Executor-)? “打印生成的服务产生的每个线程必须关闭之前”是什么意思?我不明白这一点。你至少能用代码来编辑你的问题,即使你不会发布代码来证明延迟? –

回答

0

我无法重现您提到的延迟。

由于documentedService使用“某些默认执行程序”来执行由createTask方法创建的Task。您可以配置执行,如果你想修改默认的行为,例如:

s.setExecutor(Executors.newCachedThreadPool(runnable -> { 
    Thread t = new Thread(runnable); 
    t.setDaemon(true); 
    return t ; 
}); 

很明显,你可以请根据您的具体要求(这并不完全清楚你的问题),你选择的任何遗嘱执行人。例如,如果你想限制(说)5线程,你可以做

s.setExecutor(Executors.newFixedThreadPool(5, runnable -> { 
    Thread t = new Thread(runnable); 
    t.setDaemon(true); 
    return t ; 
}); 

这是一个SSCCE。我加了一些记录到跟踪服务的状态,并显示该线程正在使用的任务:

import java.util.concurrent.Executors; 

import javafx.application.Application; 
import javafx.concurrent.Service; 
import javafx.concurrent.Task; 
import javafx.scene.Scene; 
import javafx.scene.control.TextField; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 

public class ServiceRestartTest extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     Service<Void> s = new Service<Void>(){ 
      @Override 
      protected Task<Void> createTask() { 
       return new Task<Void>(){ 
        @Override 
        protected Void call() throws Exception { 
         //DOWNLOAD DATA 
         System.out.println("New task on thread "+Thread.currentThread()); 
         System.out.println("1"); 
         Thread.sleep(1000); 
         System.out.println("2"); 
         Thread.sleep(1000); 
         System.out.println("3"); 
         Thread.sleep(1000); 
         return null; 
        } 
       }; 
      } 
     }; 
     s.setExecutor(Executors.newCachedThreadPool(runnable -> { 
      Thread t = new Thread(runnable); 
      t.setDaemon(true); 
      return t; 
     })); 

     s.stateProperty().addListener((obs, oldState, newState) -> System.out.println(newState)); 

     TextField textField = new TextField(); 
     textField.setOnKeyReleased(e -> s.restart()); 

     primaryStage.setScene(new Scene(new StackPane(textField), 350, 120)); 
     primaryStage.show(); 

    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

输出,只显示两个线程使用:

 
SCHEDULED 
New task on thread Thread[Thread-5,5,main] 
1 
RUNNING 
CANCELLED 
READY 
SCHEDULED 
New task on thread Thread[Thread-6,5,main] 
1 
RUNNING 
CANCELLED 
READY 
SCHEDULED 
New task on thread Thread[Thread-6,5,main] 
1 
RUNNING 
CANCELLED 
READY 
SCHEDULED 
New task on thread Thread[Thread-6,5,main] 
1 
RUNNING 
CANCELLED 
READY 
SCHEDULED 
New task on thread Thread[Thread-6,5,main] 
1 
RUNNING 
CANCELLED 
READY 
SCHEDULED 
New task on thread Thread[Thread-6,5,main] 
1 
RUNNING 
2 
3 
SUCCEEDED 
+0

似乎将执行程序设置为Executors.newSingleThreadScheduledExecutor()解决了太多线程问题 – user2468425

+0

我试过了您的版本,但它在我的情况下不起作用,无论如何谢谢您帮助我做事情正确的方式。如果你编辑你的答案,我会投票。 – user2468425

+1

你大概不需要一个安排的执行者(在你的调度问题中至少没有任何问题)。如果你真的需要两件事情异步发生,单线程执行程序会导致问题。缓存的线程池重用线程:我只看到两个线程在这里创建。 –