- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我试图了解线程如何在Java中工作。这是一个简单的数据库请求,返回一个ResultSet。我正在使用JavaFx。
package application;
import java.sql.ResultSet;
import java.sql.SQLException;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
public class Controller{
@FXML
private Button getCourseBtn;
@FXML
private TextField courseId;
@FXML
private Label courseCodeLbl;
private ModelController mController;
private void requestCourseName(){
String courseName = "";
Course c = new Course();
c.setCCode(Integer.valueOf(courseId.getText()));
mController = new ModelController(c);
try {
ResultSet rs = mController.<Course>get();
if(rs.next()){
courseCodeLbl.setText(rs.getString(1));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// return courseName;
}
public void getCourseNameOnClick(){
try {
// courseCodeLbl.setText(requestCourseName());
Thread t = new Thread(new Runnable(){
public void run(){
requestCourseName();
}
}, "Thread A");
t.start();
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
最佳答案
JavaFX的线程规则
线程和JavaFX有两个基本规则:
任何修改或访问场景图一部分的节点状态的代码都必须在JavaFX应用程序线程上执行。某些其他操作(例如,创建新的Stage
)也受此规则约束。
任何可能需要很长时间才能运行的代码都应在后台线程上执行(即不在FX Application线程上执行)。
第一条规则的原因是,像大多数UI工具包一样,编写框架时对场景图的元素状态没有任何同步。添加同步会导致性能损失,这对于UI工具包来说是一笔过高的费用。因此,只有一个线程可以安全地访问此状态。由于UI线程(JavaFX的FX应用程序线程)需要访问此状态才能呈现场景,因此FX Application线程是唯一可以访问“实时”场景图状态的线程。在JavaFX 8和更高版本中,受此规则约束的大多数方法都会执行检查,并在违反该规则时抛出运行时异常。 (这与Swing形成对比,在Swing中,您可以编写“非法”代码,并且看起来可能运行良好,但实际上在任意时间都容易出现随机且不可预测的故障。)这是导致您使用IllegalStateException
的原因看到:您正在从FX Application Thread之外的其他线程调用courseCodeLbl.setText(...)
。
第二条规则的原因是,FX Application Thread不仅负责处理用户事件,还负责渲染场景。因此,如果您在该线程上执行了长时间运行的操作,则在该操作完成之前,UI才会呈现,并且将对用户事件无响应。虽然这不会产生异常或导致损坏的对象状态(如违反规则1那样),但(最多)它会带来不良的用户体验。
因此,如果您有一个长时间运行的操作(例如访问数据库),需要在完成时更新UI,则基本计划是在后台线程中执行长时间运行的操作,并在操作完成时返回操作结果完成,然后在UI(FX应用程序)线程上安排对UI的更新。所有单线程UI工具箱都具有执行此操作的机制:在JavaFX中,您可以通过调用Platform.runLater(Runnable r)
在FX Application Thread上执行r.run()
来执行此操作。 (在Swing中,您可以在AWT事件分发线程上调用SwingUtilities.invokeLater(Runnable r)
来执行r.run()
。)JavaFX(请参见此答案的后面)还提供了一些更高级别的API,用于管理与FX Application Thread的通信。
多线程通用良好实践
使用多个线程的最佳实践是将要在“用户定义”线程上执行的代码构造为具有某种固定状态初始化的对象,并具有执行该操作的方法,并在完成时返回一个对象代表结果。对于初始化状态和计算结果使用不可变的对象是非常理想的。这里的想法是尽可能消除任何可变状态从多个线程可见的可能性。从数据库访问数据非常适合以下习惯用法:您可以使用数据库访问的参数(搜索项等)来初始化“ worker”对象。执行数据库查询并获取结果集,使用该结果集填充域对象的集合,然后最后返回该集合。
在某些情况下,有必要在多个线程之间共享可变状态。当绝对必须这样做时,您需要仔细同步对该状态的访问,以避免观察到状态不一致的状态(还有其他更细微的问题需要解决,例如状态的活跃性等)。强烈建议在需要时使用高级库来为您管理这些复杂性。
使用javafx.concurrent API
JavaFX提供了一个concurrency API,旨在用于在后台线程中执行代码,而API专门用于在代码执行完成时(或执行期间)更新JavaFX UI。此API旨在与java.util.concurrent
API进行交互,该javafx.concurrent
提供了用于编写多线程代码的常规工具(但没有UI挂钩)。 Task
中的键类是call()
,它表示打算在后台线程上执行的单个一次性工作单元。此类定义单个抽象方法Task
,该方法不带任何参数,返回结果并可能引发检查的异常。 Runnable
只需调用run()
即可通过其call()
方法实现Task
。 updateProgress(...)
也有保证可以更新FX Application Thread上状态的方法的集合,例如updateMessage(...)
,state
等。它定义了一些可观察的属性(例如value
和setOnSucceeded(...)
):侦听器这些属性的设置将在FX Application Thread上得到通知。最后,有一些方便的方法可以注册处理程序(setOnFailed(...)
,Data Access Object design pattern等);通过这些方法注册的所有处理程序也将在FX Application Thread上调用。
因此,从数据库检索数据的一般公式为:
创建一个Task
来处理对数据库的调用。
用执行数据库调用所需的任何状态初始化Task
。
实现任务的call()
方法以执行数据库调用,并返回调用结果。
向任务注册一个处理程序,以在完成后将结果发送到UI。
在后台线程上调用任务。
对于数据库访问,我强烈建议将实际的数据库代码封装在一个对UI一无所知的单独类中(Task
)。然后,让任务调用数据访问对象上的方法。
因此,您可能会有一个这样的DAO类(请注意,这里没有UI代码):
public class WidgetDAO {
// In real life, you might want a connection pool here, though for
// desktop applications a single connection often suffices:
private Connection conn ;
public WidgetDAO() throws Exception {
conn = ... ; // initialize connection (or connection pool...)
}
public List<Widget> getWidgetsByType(String type) throws SQLException {
try (PreparedStatement pstmt = conn.prepareStatement("select * from widget where type = ?")) {
pstmt.setString(1, type);
ResultSet rs = pstmt.executeQuery();
List<Widget> widgets = new ArrayList<>();
while (rs.next()) {
Widget widget = new Widget();
widget.setName(rs.getString("name"));
widget.setNumberOfBigRedButtons(rs.getString("btnCount"));
// ...
widgets.add(widget);
}
return widgets ;
}
}
// ...
public void shutdown() throws Exception {
conn.close();
}
}
public class MyController {
private WidgetDAO widgetAccessor ;
// java.util.concurrent.Executor typically provides a pool of threads...
private Executor exec ;
@FXML
private TextField widgetTypeSearchField ;
@FXML
private TableView<Widget> widgetTable ;
public void initialize() throws Exception {
widgetAccessor = new WidgetDAO();
// create executor that uses daemon threads:
exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
}
// handle search button:
@FXML
public void searchWidgets() {
final String searchString = widgetTypeSearchField.getText();
Task<List<Widget>> widgetSearchTask = new Task<List<Widget>>() {
@Override
public List<Widget> call() throws Exception {
return widgetAccessor.getWidgetsByType(searchString);
}
};
widgetSearchTask.setOnFailed(e -> {
widgetSearchTask.getException().printStackTrace();
// inform user of error...
});
widgetSearchTask.setOnSucceeded(e ->
// Task.getValue() gives the value returned from call()...
widgetTable.getItems().setAll(widgetSearchTask.getValue()));
// run the task using a thread from the thread pool:
exec.execute(widgetSearchTask);
}
// ...
}
widgetTable.setItems(...)
中,该
Task
在后台线程上运行(通过访问器),以防止阻塞UI(上面的规则2)。实际上,使用
setOnSucceeded(...)
的便捷回调方法
API docs for Task
(满足规则1)在FX Application线程上执行对UI(
progress
)的更新。
public class MyDAO {
private Connection conn ;
// constructor etc...
public Course getCourseByCode(int code) throws SQLException {
try (PreparedStatement pstmt = conn.prepareStatement("select * from course where c_code = ?")) {
pstmt.setInt(1, code);
ResultSet results = pstmt.executeQuery();
if (results.next()) {
Course course = new Course();
course.setName(results.getString("c_name"));
// etc...
return course ;
} else {
// maybe throw an exception if you want to insist course with given code exists
// or consider using Optional<Course>...
return null ;
}
}
}
// ...
}
final int courseCode = Integer.valueOf(courseId.getText());
Task<Course> courseTask = new Task<Course>() {
@Override
public Course call() throws Exception {
return myDAO.getCourseByCode(courseCode);
}
};
courseTask.setOnSucceeded(e -> {
Course course = courseTask.getCourse();
if (course != null) {
courseCodeLbl.setText(course.getName());
}
});
exec.execute(courseTask);
关于java - 使用线程发出数据库请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34122920/
我将 Bootstrap 与 css 和 java 脚本结合使用。在不影响前端代码的情况下,我真的很难在css中绘制这个背景。在许多问题中,人们将宽度和高度设置为 0%。但是由于我的导航栏,我不能使用
我正在用 c 编写一个程序来读取文件的内容。代码如下: #include void main() { char line[90]; while(scanf("%79[^\
我想使用 javascript 获取矩阵数组的所有对 Angular 线。假设输入输出如下: input = [ [1,2,3], [4,5,6], [7,8,9], ] output =
可以用pdfmake绘制lines,circles和other shapes吗?如果是,是否有documentation或样本?我想用jsPDF替换pdfmake。 最佳答案 是的,有可能。 pdfm
我有一个小svg小部件,其目的是显示角度列表(参见图片)。 现在,角度是线元素,仅具有笔触,没有填充。但是现在我想使用一种“内部填充”颜色和一种“笔触/边框”颜色。我猜想line元素不能解决这个问题,
我正在为带有三角对象的 3D 场景编写一个非常基本的光线转换器,一切都工作正常,直到我决定尝试从场景原点 (0/0/0) 以外的点转换光线。 但是,当我将光线原点更改为 (0/1/0) 时,相交测试突
这个问题已经有答案了: Why do people write "#!/usr/bin/env python" on the first line of a Python script? (22 个回
如何使用大约 50 个星号 * 并使用 for 循环绘制一条水平线?当我尝试这样做时,结果是垂直(而不是水平)列出 50 个星号。 public void drawAstline() { f
这是一个让球以对角线方式下降的 UI,但球保持静止;线程似乎无法正常工作。你能告诉我如何让球移动吗? 请下载一个球并更改目录,以便程序可以找到您的球的分配位置。没有必要下载足球场,但如果您愿意,也可以
我在我的一个项目中使用 Jmeter 和 Ant,当我们生成报告时,它会在报告中显示 URL、#Samples、失败、成功率、平均时间、最短时间、最长时间。 我也想在报告中包含 90% 的时间线。 现
我有一个不寻常的问题,希望有人能帮助我。我想用 Canvas (android) 画一条 Swing 或波浪线,但我不知道该怎么做。它将成为蝌蚪的尾部,所以理想情况下我希望它的形状更像三角形,一端更大
这个问题已经有答案了: Checking Collision of Shapes with JavaFX (1 个回答) 已关闭 8 年前。 我正在使用 JavaFx 8 库。 我的任务很简单:我想检
如何按编号的百分比拆分文件。行数? 假设我想将我的文件分成 3 个部分(60%/20%/20% 部分),我可以手动执行此操作,-_-: $ wc -l brown.txt 57339 brown.tx
我正在努力实现这样的目标: 但这就是我设法做到的。 你能帮我实现预期的结果吗? 更新: 如果我删除 bootstrap.css 依赖项,问题就会消失。我怎样才能让它与 Bootstrap 一起工作?
我目前正在构建一个网站,但遇到了 transform: scale 的问题。我有一个按钮,当用户将鼠标悬停在它上面时,会发生两件事: 背景以对 Angular 线“扫过” 按钮标签颜色改变 按钮稍微变
我需要使用直线和仿射变换绘制大量数据点的图形(缩放图形以适合 View )。 目前,我正在使用 NSBezierPath,但我认为它效率很低(因为点在绘制之前被复制到贝塞尔路径)。通过将我的数据切割成
我正在使用基于 SVM 分类的 HOG 特征检测器。我可以成功提取车牌,但提取的车牌除了车牌号外还有一些不必要的像素/线。我的图像处理流程如下: 在灰度图像上应用 HOG 检测器 裁剪检测到的区域 调
我有以下图片: 我想填充它的轮廓(即我想在这张图片中填充线条)。 我尝试了形态学闭合,但使用大小为 3x3 的矩形内核和 10 迭代并没有填满整个边界。我还尝试了一个 21x21 内核和 1 迭代,但
我必须找到一种算法,可以找到两组数组之间的交集总数,而其中一个数组已排序。 举个例子,我们有这两个数组,我们向相应的数字画直线。 这两个数组为我们提供了总共 7 个交集。 有什么样的算法可以帮助我解决
简单地说 - 我想使用透视投影从近裁剪平面绘制一条射线/线到远裁剪平面。我有我认为是使用各种 OpenGL/图形编程指南中描述的方法通过单击鼠标生成的正确标准化的世界坐标。 我遇到的问题是我的光线似乎
我是一名优秀的程序员,十分优秀!