2016-07-29 74 views
0

我一直在學習關於JavaFX的任務和使用這些用Platform.runLater或任務的updateValue方法等,但應用程序線程進行通信,我Task需要知道當用戶按下一個按鈕GUI,因爲這可能會改變任務的updateValue方法返回的值。我如何去做這件事?我知道如何響應單線程應用程序上的按鈕事件,但我不確定如何以線程安全的方式處理它。的JavaFX應用程序線程任務間通信

更新:

這是我到目前爲止,這是實現按鈕事件的一個明智的辦法?

import javafx.application.Application; 
import javafx.concurrent.Task; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.canvas.GraphicsContext; 
import javafx.scene.image.PixelWriter; 
import javafx.scene.image.PixelFormat; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.VBox; 
import javafx.scene.control.Button; 

import java.nio.IntBuffer; 

public class TaskExample extends Application { 

    private Canvas canvas; 
    private PixelWriter pixel_writer; 

    @Override 
    public void start(Stage primaryStage) throws Exception { 

     canvas = new Canvas(256, 256); 
     pixel_writer = canvas.getGraphicsContext2D().getPixelWriter(); 

     MyTask task = new MyTask(); 

     task.valueProperty().addListener((c) -> { 
      if(task.getValue() != null) { 
       update(task.getValue()); 
      } 
     });  

     Thread thread = new Thread(task); 
     thread.setDaemon(true);  
     thread.start(); 

     Button button = new Button("Button 1"); 

     // On the button click event it calls the eventFired() method 
     button.setOnAction((event) -> { 
      task.eventFired(); 
     }); 

     Pane pane = new VBox(); 
     pane.getChildren().addAll(canvas, button); 

     primaryStage.setScene(new Scene(pane)); 
     primaryStage.show();  

    } 


    public void update(IntBuffer data) { 
     pixel_writer.setPixels(
      0, 
      0, 
      256, 
      256, 
      PixelFormat.getIntArgbInstance(), 
      data, 
      256 
     ); 
    } 

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

    class MyTask extends Task<IntBuffer> { 

     public void eventFired() { 
      System.out.println("Event fired"); 
     } 

     public void update(IntBuffer data) { 
      updateValue(data); 
     } 

     @Override 
     protected IntBuffer call() throws InterruptedException { 

      while(true) { 
       for (int i=0; i<3; i++) { 
        Thread.sleep(1000); 
        IntBuffer data = IntBuffer.allocate(256*256);     
        for(int j=0; j<256*256; j++) { 
         switch(i) { 
          case 0: data.put(0xFF0000FF); break; 
          case 1: data.put(0xFF00FF00); break; 
          case 2: data.put(0xFFFF0000); break; 
         } 

        } 
        data.rewind();      
        update(data); 
       } 
      } 

     } 

    } 
} 
+0

您發佈的代碼沒有任何問題,但實際上並沒有描述您所描述的內容:即兩個線程不會互動。當您按下按鈕時,任務將在FX應用程序線程上向控制檯顯示一條消息。但沒有任何更改會影響後臺線程上發生的情況。所以,如果你想問「這項工作」,你需要實際展示一些你所描述的代碼。 –

+0

啊,我沒有意識到他們沒有互動。如何在後臺線程上運行'eventFired'方法? – soarjay

+0

你不能在後臺線程上運行它。但大概只是給控制檯發送消息並不是你真正想要做的。你想做些什麼(除了你知道這個階段的人以外沒有人會改變'call'方法中發生的事情,不是嗎? –

回答

1

我在這裏要做的是想辦法重構你在做什麼以避免兩個不同線程之間的通信。例如,不要將自己在做什麼看作是隨着UI進展而更新UI的一項長期運行任務,而應將其視爲一系列單獨任務,每個任務在完成時更新UI。該ScheduledService class提供機械管理這些任務,並在一個乾淨和安全的方式和他們的FX應用程序線程間通信:

import java.nio.IntBuffer; 
import java.util.Arrays; 

import javafx.application.Application; 
import javafx.concurrent.ScheduledService; 
import javafx.concurrent.Task; 
import javafx.scene.Scene; 
import javafx.scene.canvas.Canvas; 
import javafx.scene.control.Button; 
import javafx.scene.image.PixelFormat; 
import javafx.scene.image.PixelWriter; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class TaskExample extends Application { 

    private Canvas canvas; 
    private PixelWriter pixel_writer; 

    @Override 
    public void start(Stage primaryStage) throws Exception { 

     canvas = new Canvas(256, 256); 
     pixel_writer = canvas.getGraphicsContext2D().getPixelWriter(); 


     MyService service = new MyService(); 
     service.setPeriod(Duration.seconds(1)); 

     service.valueProperty().addListener((ols, oldData, newData) -> { 
      if(newData != null) { 
       update(newData); 
      } 
     });  
     service.start(); 

     Button button = new Button("Button 1"); 


     Pane pane = new VBox(); 
     pane.getChildren().addAll(canvas, button); 

     primaryStage.setScene(new Scene(pane)); 
     primaryStage.show();  

    } 


    public void update(IntBuffer data) { 
     pixel_writer.setPixels(
      0, 
      0, 
      256, 
      256, 
      PixelFormat.getIntArgbInstance(), 
      data, 
      256 
     ); 
    } 

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

    class MyService extends ScheduledService<IntBuffer> { 

     // both instance variables accessed only on FX Application Thread: 

     private final int[] colors = {0xFF0000FF, 0xFF00FF00, 0xFFFF0000} ; 

     private int count = -1 ; 

     @Override 
     protected Task<IntBuffer> createTask() { 
      // invoked on FX Application Thread 
      count = (count + 1) % colors.length ; 
      return new MyTask(colors[count]); 
     } 
    } 

    class MyTask extends Task<IntBuffer> { 

     private final int color ; 

     MyTask(int color) { 
      // invoked on FX Application Thread: 
      this.color = color ; 
     } 

     @Override 
     protected IntBuffer call() { 
      // invoked on background thread: 
      IntBuffer data = IntBuffer.allocate(256*256); 
      int[] a = new int[256*256]; 
      Arrays.fill(a, color); 
      data.put(a, 0, a.length); 
      data.rewind(); 
      return data ; 
     } 

    } 
} 

您還沒有非常具體的關於UI應該如何與後臺交互線程,但如果您想在按下按鈕時更改服務的行爲,現在您將更改在FX應用程序線程上調用的createTask方法的行爲,而不是更改已在運行的方法的行爲在不同的線程上。這可以避免任何有關同步的「低級」問題。

例如:

class MyService extends ScheduledService<IntBuffer> { 

    // all instance variables accessed only on FX Application Thread: 

    private final int[][] colors = { 
      {0xFF0000FF, 0xFF00FF00, 0xFFFF0000}, 
      {0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00} 
    }; 

    private int count = -1 ; 
    private int scheme = 0 ; 

    @Override 
    protected Task<IntBuffer> createTask() { 
     // invoked on FX Application Thread 
     count = (count + 1) % colors[scheme].length ; 
     return new MyTask(colors[scheme][count]); 
    } 

    public void changeScheme() { 
     // invoked on FX Application Thread 
     scheme = (scheme + 1) % colors.length ; 
    } 
} 

,然後就

button.setOnAction(e -> service.changeScheme()); 

添加到service.restart();這裏調用將強制更改爲儘快發生:

button.setOnAction(e -> { 
    service.changeScheme(); 
    service.restart(); 
}); 

有幾乎總是一種重構代碼的方式來利用庫類像這樣來避免線程之間的低級通信。