2017-02-24 60 views
1

好吧,所以我在Java編程方面並不陌生,但我在使用Java程序中的線程方面很新穎。我在學校,剛剛完成了關於線程和Java網絡的章節。我正在編寫一個將貸款信息(年利率,年數和貸款金額)發送到服務器的客戶端GUI。服務器有自己的圖形用戶界面(GUI),計算每月付款和貸款總額,並將其發送回客戶端並顯示給用戶以及更新服務器GUI。有沒有辦法在JavaFX的不同類文件中使用線程?

書中有這樣的代碼爲例:

public class Server extends Application { 
/** Variables */ 
private TextArea textArea = new TextArea(); 
private double rate; 
private int year; 
private double loan; 

public void start(Stage serverStage) 
{ 
    // Creating server GUI 
    Scene scene = new Scene(new ScrollPane(textArea), 400, 200); 
    serverStage.setTitle("Server"); 
    serverStage.setScene(scene); 
    serverStage.show(); 

    new Thread(() ->{ 

     try 
     { 
      // create server socket 
      ServerSocket serverSocket = new ServerSocket(8000); 
      textArea.appendText("Server started at " + new Date() + "\n"); 

      while(true) 
      { 
       // listen for a connection request 
       Socket socket = serverSocket.accept(); 

       Platform.runLater(() -> { 
        InetAddress inetAddress = socket.getInetAddress(); 
        textArea.appendText("Connected to " + inetAddress.getHostAddress() + " at " + new Date() + "\n"); 
       }); 


       // create and start a new thread for every connection 
       new Thread(new HandleAClient(socket)).start(); 

      } 
     } 
     catch(IOException ex) 
     { 
      ex.printStackTrace(); 
     } 
    }).start(); 
} 

class HandleAClient implements Runnable { 

    private Socket socket; // A connected socket 
    private double rate; 
    private int year; 
    private double loan; 

    /** costruct a thread */ 
    public HandleAClient(Socket socket) 
    { 
     this.socket = socket; 
    } 

    /** run a thread */ 
    public void run(){ 
     try 
     { 
      // create data input and output streams 
      DataInputStream inputFromClient = new DataInputStream(socket.getInputStream()); 
      DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream()); 

      // continuously serve the client 
      while(true) { 
       // read data from client 

       rate = inputFromClient.readDouble(); 
       year = inputFromClient.readInt(); 
       loan = inputFromClient.readDouble(); 


       // calculate monthly payment of loan and total payment 

       outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan)); 
       outputToClient.writeDouble(calculateTotalPayment(rate, year, loan)); 

       Platform.runLater(() -> { 
        textArea.appendText("The rate is : " + rate + "\n"); 
        textArea.appendText("The number of years is: " + year + "\n"); 
        textArea.appendText("Loan amount is: " + loan + "\n\n");}); 




      } 
     } 
     catch(IOException e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

// calculateMonthlyPayment method calculates the monthly payment of a loan given 
// the required information 

public double calculateMonthlyPayment(double interestRate, int years, double loanAmt) 
{ 
    double monthlyRate; 
    int termInMonths; 
    double monthlyPayment; 

    // Convert the interest rate to a decimal 
    interestRate = interestRate/100; 

    // convert annual interest rate to monthly interest rate 
    monthlyRate = interestRate/12; 

    // calculate the term in months which is years * 12 
    termInMonths = years * 12; 

    monthlyPayment = (loanAmt*monthlyRate)/(1-Math.pow(1+monthlyRate, -termInMonths)); 

    return monthlyPayment; 
} 



// method that calculates and returns the total payment of the loan 
public double calculateTotalPayment(double rate, int year, double loan) 
{ 
    double totalPayment; 
    double monthlyPay; 

    monthlyPay = calculateMonthlyPayment(rate, year, loan); 


    totalPayment = monthlyPay * 12 * year; 

    return totalPayment; 
    } 

} 

正如您在示例代碼中看到,他們(本書的作者)使用一個新的線程來能夠的文本追加服務器GUI。然而,爲了能夠處理多個客戶端,在while循環內部創建了一個新線程來處理每個客戶端。

我試圖創建HandleAClient類作爲一個單獨的Java類,而不是在服務器級別將其插入的,但是這導致服務器GUI不與Platform.runLater代碼

Platform.runLater(() -> { 
        textArea.appendText("The rate is : " + rate + "\n"); 
        textArea.appendText("The number of years is: " + year + "\n"); 
        textArea.appendText("Loan amount is: " + loan + "\n\n");}); 

所以我的問題更新:爲什麼它在HandleAClient類位於Server類內部時起作用,而當HandleAClient類位於擴展Server的單獨Java類文件中時不起作用?我認爲它必須對線程做些什麼?爲了能夠在自己的Java類文件中擁有HandleAClient類,我該怎麼做?

我很好奇,並試圖很好地理解線程是如何工作的。先謝謝你。

UPDATE 這是沒有爲我工作的獨立類。我擴展了Server類,並且在Server類中保留了TextArea字段。

class HandleAClient extends Server implements Runnable { 

private Socket socket; // A connected socket 
private double rate; 
private int year; 
private double loan; 


public HandleAClient(Socket socket) 
{ 
    this.socket = socket; 
} 

/** run a thread */ 
public void run(){ 
    try 
    { 
     // create data input and output streams 
     DataInputStream inputFromClient = new DataInputStream(socket.getInputStream()); 
     DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream()); 

     // continuously serve the client 
     while(true) { 
      // read data from client 

      rate = inputFromClient.readDouble(); 
      year = inputFromClient.readInt(); 
      loan = inputFromClient.readDouble(); 


      // calculate monthly payment of loan and total payment 

      outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan)); 
      outputToClient.writeDouble(calculateTotalPayment(rate, year, loan)); 

      Platform.runLater(() -> { 
       textArea.appendText("The rate is : " + rate + "\n"); 
       textArea.appendText("The number of years is: " + year + "\n"); 
       textArea.appendText("Loan amount is: " + loan + "\n\n");}); 

     } 
    } 
    catch(IOException e) 
    { 
     e.printStackTrace(); 
    } 
    } 
} 

Platform.runLater中的代碼沒有出現在服務器GUI中,因爲它是在類位於Server類內部時出現的。我想明白爲什麼會發生這種情況。

+0

我不確定我是否理解這個問題。 'HandleAClient'類應該可以獨立運行,只要它能夠訪問它需要訪問的所有'Server'類的字段(真正的內部類和頂級類之間的唯一區別是內部類可以訪問其周圍類的字段)。當你把它作爲一個頂級的類來實現時,你是如何讓它訪問'textArea'的?你可以發佈獨立(頂級)HandleAClient類的完整版本嗎? –

+0

是的,我更新了我原來的帖子,包括沒有工作的班級。基本上我在Server類中保護了textArea字段,並將Server類擴展爲HandleAClient類。 – j2k1218

+0

儘管您在此處更新的文本區域與您在UI中顯示的文本區域不同。你需要更新屬於顯示UI的'Server'實例的那個。 –

回答

0

你需要你的HandleAClient實例來更新屬於創建它們的Server對象文本區域:屬於該Server實例textArea是顯示在用戶界面中的一個。通過您的設置,每個HandleAClient實例都有自己的textArea,並且每個實例都更新自己的textArea。當然,這些都沒有被展示過。

HandleAClient擴展Server對我沒有意義。您需要執行的操作(無論是否具有該繼承)爲HandleAClient提供了更新屬於Server的文本區域的方法。最簡單的(但不一定是最好的)方式就是文本區域傳遞給HandleAClient實例:當然

class HandleAClient implements Runnable { 

    private Socket socket; // A connected socket 
    private double rate; 
    private int year; 
    private double loan; 

    private final TextArea textArea ; 

    public HandleAClient(Socket socket, TextArea textArea) 
    { 
     this.socket = socket; 
     this.textArea = textArea ; 
    } 

    /** run a thread */ 
    public void run(){ 
     try 
     { 
      // create data input and output streams 
      DataInputStream inputFromClient = new DataInputStream(socket.getInputStream()); 
      DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream()); 

      // continuously serve the client 
      while(true) { 
       // read data from client 

       rate = inputFromClient.readDouble(); 
       year = inputFromClient.readInt(); 
       loan = inputFromClient.readDouble(); 


       // calculate monthly payment of loan and total payment 

       outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan)); 
       outputToClient.writeDouble(calculateTotalPayment(rate, year, loan)); 

       Platform.runLater(() -> { 
        textArea.appendText("The rate is : " + rate + "\n"); 
        textArea.appendText("The number of years is: " + year + "\n"); 
        textArea.appendText("Loan amount is: " + loan + "\n\n");}); 

      } 
     } 
     catch(IOException e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

然後在Server修改它

while(true) 
{ 
    // listen for a connection request 
    Socket socket = serverSocket.accept(); 

    Platform.runLater(() -> { 
     InetAddress inetAddress = socket.getInetAddress(); 
     textArea.appendText("Connected to " + inetAddress.getHostAddress() + " at " + new Date() + "\n"); 
    }); 


    // create and start a new thread for every connection 
    new Thread(new HandleAClient(socket, textArea)).start(); 

} 

感覺有點不自然讓HandleAClient類(它負責通信)依靠引用TextArea(這是UI特定的)。更自然的方法如下。

我可能會定義一個簡單的類來表示利率,一年,貸款:

public class LoanData { 

    private final double rate ; 
    private final int year ; 
    private final double loan ; 

    public LoanData(double rate, int year, double loan) { 
     this.rate = rate ; 
     this.year = year ; 
     this.loan = loan ; 
    } 

    public double getRate() { 
     return rate ; 
    } 
    public int getYear() { 
     return year ; 
    } 
    public double getLoan() { 
     return loan ; 
    } 
} 

然後給HandleAClientConsumer<LoanData>用於處理貸款數據:

class HandleAClient implements Runnable { 

    private Socket socket; // A connected socket 

    private final Consumer<LoanData> dataProcessor ; 

    public HandleAClient(Socket socket, Consumer<LoanData> dataProcessor) 
    { 
     this.socket = socket; 
     this.dataProcessor = dataProcessor ; 
    } 

    /** run a thread */ 
    public void run(){ 
     try 
     { 
      // create data input and output streams 
      DataInputStream inputFromClient = new DataInputStream(socket.getInputStream()); 
      DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream()); 

      // continuously serve the client 
      while(true) { 
       // read data from client 

       double rate = inputFromClient.readDouble(); 
       double year = inputFromClient.readInt(); 
       double loan = inputFromClient.readDouble(); 


       // calculate monthly payment of loan and total payment 

       outputToClient.writeDouble(calculateMonthlyPayment(rate, year, loan)); 
       outputToClient.writeDouble(calculateTotalPayment(rate, year, loan)); 

       dataProcessor.accept(new LoanData(rate, year, loan)); 

      } 
     } 
     catch(IOException e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

現在服務器做

Consumer<LoanData> textAreaUpdater = 
    loanData -> Platform.runLater(() -> { 
     textArea.appendText("The rate is : " + loanData.getRate() + "\n"); 
     textArea.appendText("The number of years is: " + loanData.getYear() + "\n"); 
     textArea.appendText("Loan amount is: " + loanData.getLoan() + "\n\n"); 
    }); 

new Thread(new HandleAClient(socket, textAreaUpdater)).start(); 

這使得UI數據功能在UI類中正確隱蔽。

+0

謝謝你解決了! – j2k1218

相關問題