有没有办法在 JavaFX 中跨不同的 class 文件使用线程?
Is there a way to use a thread across different class files in JavaFX?
好的,所以我对 Java 编程并不陌生,但我对在我的 Java 程序中使用线程很陌生。我在学校,刚刚完成有关线程和 Java 网络的章节。我正在编写一个客户端 GUI 程序,用于将贷款信息(年利率、年数和贷款金额)发送到服务器。服务器有自己的 GUI 并计算每月还款额和贷款总付款额并将其发送回客户端并显示给用户并更新服务器 GUI。
书上有这段代码作为例子:
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 class 创建为单独的 Java class 而不是将其插入服务器 class 但这导致服务器 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 class 位于服务器 class 内部时它可以工作,而当 HandleAClient class 位于单独的 Java class 文件扩展服务器?我认为它必须对线程做些什么?我要改变什么才能让 HandleAClient class 在它自己的 Java class 文件中?
我很好奇并试图对线程的工作原理有一个很好的理解。提前谢谢你。
更新
这是对我不起作用的独立 class。我扩展了服务器 class 并在服务器 class.
中保护了 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 中的代码不会像 class 在服务器 class 中时那样出现在服务器 GUI 中。我想了解为什么会发生这种情况。
您需要 HandleAClient
实例来更新属于创建它们的 Server
对象的文本区域:属于 Server
实例的 textArea
是UI 中显示的一个。通过您的设置,每个 HandleAClient
实例都有自己的 textArea
,并且每个实例都更新自己的 textArea
。当然,其中 None 曾经显示过。
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
class(负责通信)依赖引用TextArea
(也就是UI)感觉有点不自然-具体的)。更自然的做法如下。
我可能会定义一个简单的 class 来表示利率、年份和贷款:
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 ;
}
}
然后给HandleAClient
class一个Consumer<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 class.
中正确隔离
好的,所以我对 Java 编程并不陌生,但我对在我的 Java 程序中使用线程很陌生。我在学校,刚刚完成有关线程和 Java 网络的章节。我正在编写一个客户端 GUI 程序,用于将贷款信息(年利率、年数和贷款金额)发送到服务器。服务器有自己的 GUI 并计算每月还款额和贷款总付款额并将其发送回客户端并显示给用户并更新服务器 GUI。
书上有这段代码作为例子:
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 class 创建为单独的 Java class 而不是将其插入服务器 class 但这导致服务器 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 class 位于服务器 class 内部时它可以工作,而当 HandleAClient class 位于单独的 Java class 文件扩展服务器?我认为它必须对线程做些什么?我要改变什么才能让 HandleAClient class 在它自己的 Java class 文件中?
我很好奇并试图对线程的工作原理有一个很好的理解。提前谢谢你。
更新 这是对我不起作用的独立 class。我扩展了服务器 class 并在服务器 class.
中保护了 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 中的代码不会像 class 在服务器 class 中时那样出现在服务器 GUI 中。我想了解为什么会发生这种情况。
您需要 HandleAClient
实例来更新属于创建它们的 Server
对象的文本区域:属于 Server
实例的 textArea
是UI 中显示的一个。通过您的设置,每个 HandleAClient
实例都有自己的 textArea
,并且每个实例都更新自己的 textArea
。当然,其中 None 曾经显示过。
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
class(负责通信)依赖引用TextArea
(也就是UI)感觉有点不自然-具体的)。更自然的做法如下。
我可能会定义一个简单的 class 来表示利率、年份和贷款:
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 ;
}
}
然后给HandleAClient
class一个Consumer<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 class.
中正确隔离