无法将JavaFx任务委托给静态方法中的新线程
我有一个普通类,它有静态方法和静态变量,带有Hibernate配置设置,方法在访问数据库后返回列表。我正在从事JavaFx的工作,最近我了解到,将Task用于耗时的操作(如在数据库中查找一长串数据等)会更好。 因此,在我的例子中,我创建了一个Task对象,在匿名内部类中编写了代码,其中包含了为登录凭证点击db的代码。任务必须返回列表的实例 我在静态方法中初始化了一个线程对象,在其构造函数中传递了任务的对象,将守护进程设置为true并启动了线程。但是,在运行应用程序后,我会收到NullPointerException无法将JavaFx任务委托给静态方法中的新线程,java,multithreading,static,javafx,task,Java,Multithreading,Static,Javafx,Task,我有一个普通类,它有静态方法和静态变量,带有Hibernate配置设置,方法在访问数据库后返回列表。我正在从事JavaFx的工作,最近我了解到,将Task用于耗时的操作(如在数据库中查找一长串数据等)会更好。 因此,在我的例子中,我创建了一个Task对象,在匿名内部类中编写了代码,其中包含了为登录凭证点击db的代码。任务必须返回列表的实例 我在静态方法中初始化了一个线程对象,在其构造函数中传递了任务的对象,将守护进程设置为true并启动了线程。但是,在运行应用程序后,我会收到NullPointe
private static SessionFactory sessionFactory = null;
private static Session session = null;
private static final Transaction transaction = null;
private static final Configuration configuration = null;
private static List list;
public static List getSelectList(final String query)
{
//list = null;
System.err.println("Inside getSelectList");
try{
final Task <List> task= new Task <List> ()
{
@Override
public List call()
{
try
{
System.err.println("Inside call");
session.beginTransaction();
list = session.createQuery(query).list();
System.err.println(list);
session.getTransaction().commit();
return list;
}
catch (Exception e)
{
session.getTransaction().rollback();
e.printStackTrace();
}
System.err.println("Outta try block");
return null;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
System.err.println("Inside handle");
list = task.getValue();
/* for (Iterator it = list.iterator();it.hasNext(); )
{
System.err.println("Inside Set on succeeded");
//System.err.println( ((Login)it.next()).getUsername());
} */
}
});
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
//list=task.getValue();
}
catch (Exception e) {e.printStackTrace();}
return list;
}
我想知道为什么会这样。为什么task.run()和使用静态列表对我有效
将列表转换为静态,删除线程实例并使用task.run();这仅仅是我为使代码正常工作所做的不同尝试的一个数字。我不知道原因。
如果有任何解释,我将不胜荣幸。提前谢谢 发生了什么事 使用任务创建一个
线程
,然后在该线程上调用start()
,将导致在所创建的线程中异步执行task.run()
只需调用task.run()
即可在当前线程中执行run()
方法
线程的代码在其行为上是不确定的。换句话说,您无法仅从代码预测结果。问题是您正在从两个不同的线程访问共享列表
实例,对访问顺序没有控制权。发生的情况如下:
list
最初为null
您可以调用getSelectList()
。
在getSelectList()中
:
list
的值设置为查询结果。(这将发生在FX应用程序线程上。)list
getSelectList()
到达其return
语句之前,您无法控制任务是否完成
因此,如果getSelectList()
在任务完成之前(以及在调用任务的onSucceeded
处理程序之前)到达它的return
语句,getSelectList()
将在任务更新它之前返回list
的值,即它将返回null
。这几乎肯定更有可能发生(因为任务正在访问数据库,速度很慢),我想这就是为什么会出现空指针异常
如果任务在getSelectList()
到达其return
语句之前完成了对其onSucceeded
处理程序的调用,那么在getSelectList()
到达return
语句之前,列表将被更新,并返回任务设置的值。这是极不可能的,即使发生这种情况,也不能保证您得到列表的“live”值(因为Java语言规范中关于线程和内存之间关系的一些复杂性)
请注意,如果从FX应用程序线程调用getSelectList()
,则保证它将返回null
,因为在getSelectList()
完成之前不可能调用onSucceeded
处理程序(因为这两个方法调用都在同一个线程上运行——FX应用程序线程)
如何修复它
首先,除非进行了适当的同步,否则应避免从不同线程访问共享变量(list
)。自己进行同步比较困难,通常应使用高级API来管理该变量。任务
API就是为此而设计的(以及通用的java.util.concurrent
API)
我通常避免在数据访问对象类中管理线程(或异常处理),只要让客户机代码在线程代码中包装对DAO的调用(如果需要)
所以(我不打算让这个方法静态,因为这通常是可怕的):
然后,通过JavaFXUI代码,您可以
DAO myDAO = ... ;
Task<List> task = new Task<List>() {
@Override
public void call() throws Exception {
return myDAO.getSelectList(...);
}
});
task.setOnSucceeded(event -> {
List list = task.getValue();
// use list to update UI...
});
task.setOnFailed(event -> {
Exception exc = task.getException();
// handle exception...
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
进一步改进
您应该使用正确键入的列表,而不是
原始类型,用于类型安全
使用而不是自己管理线程创建
注意事项所有代码都是按原样输入的,没有经过测试,因此可能会有输入错误。不过,这应该会让您了解情况。发生了什么事
使用任务创建一个线程
,然后在该线程上调用start()
,将导致在所创建的线程中异步执行task.run()
只需调用task.run()
即可在当前线程中执行run()
方法
线程中的代码在行为上是不确定的。换句话说,您无法仅从代码预测结果。问题是您正在从两个不同的线程访问共享列表实例,无法控制访问顺序。下面是发生的情况:
list
最初为null
您可以调用getSelectList()
。
在getSelectList()中
:
您创建了一个任务
将任务配置为将
public List getSelectList(String query) throws Exception {
Session session = sessionFactory.createSession();
try {
session.beginTransaction();
List list = session.createQuery(query).list();
session.getTransaction().commit();
return list ;
} catch (Exception e) {
Transaction tx = session.getTransaction();
if (tx != null) {
tx.rollback();
}
throw e ;
}
}
DAO myDAO = ... ;
Task<List> task = new Task<List>() {
@Override
public void call() throws Exception {
return myDAO.getSelectList(...);
}
});
task.setOnSucceeded(event -> {
List list = task.getValue();
// use list to update UI...
});
task.setOnFailed(event -> {
Exception exc = task.getException();
// handle exception...
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
public void getSelectList(String query,
Consumer<List> succeededHandler,
Consumer<Exception> errorHandler) {
FutureTask<List> futureTask = new FutureTask<>(() -> {
Session session = sessionFactory.getSession();
try {
session.beginTransaction();
List list = session.createQuery(query).list();
session.getTransaction().commit();
return list ;
} catch (Exception e) {
Transaction tx = session.getTransaction();
if (tx != null) {
tx.rollback();
}
throw e ;
}
});
Thread thread = new Thread(futureTask);
thread.setDaemon(true);
thread.start();
try {
List list = futureTask.get();
succeededHandler.accept(list);
} catch (Exception e) {
errorHandler.accept(e);
}
}
DAO myDAO = ... ;
String query = ... ;
myDAO.getSelectList(query,
list -> Platform.runLater(() -> {
// update UI with list ...
}),
exc -> Platform.runLater(() -> {
// handle exception...
})
);