我有一个General类,它有静态方法和静态变量,Hibernate配置设置和方法,在命中数据库后返回List。我正在研究JavaFx,我最近学会了更好地使用Task进行耗时的操作,例如点击数据库获取一长串数据等。 所以,在我的情况下,我创建了一个Task对象,在匿名内部类中编写代码,其中包含用于访问LOGIN凭据的db的代码。 Task必须返回List的实例。
我在静态方法中初始化了一个Thread对象,在其构造函数中传递了Task对象,将守护进程设置为true并启动了Thread。但是我在运行应用程序后得到NullPointerException。
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()代替它是有用的。
/*Thread th = new Thread(task);
th.setDaemon(true);
th.start();*/
task.run();
try
{
session.beginTransaction();
//List locallist = session.createSQLQuery(query).list();
list = session.createSQLQuery(query).list();
session.getTransaction().commit();
return list ;
}
catch (Exception e){
session.getTransaction().rollback();
我想知道为什么会这样。为什么task.run()和静态List一起为我工作?
将List转换为static并删除Thread实例并使用task.run();仅仅是为了让我的代码能够工作而从我这边尝试不同的数字。我不知道原因。 任何解释都会让我感到非常谦卑。提前谢谢!
答案 0 :(得分:3)
发生了什么
使用您的任务创建Thread
,然后在线程上调用start()
会导致task.run()
在您创建的主题中以异步方式执行。
只需调用task.run()
即可执行当前线程中的run()
方法。
您对线程的代码在其行为中是不确定的。换句话说,您无法单独从代码中预测结果。问题是您从两个不同的线程访问共享的list
实例,无法控制访问顺序。以下是发生的事情:
list
最初为null
。
您致电getSelectList()
。
在getSelectList()
:
list
的值设置为查询结果。 (这将发生在FX应用程序线程上。)list
因为任务是异步执行的,所以在getSelectList()
到达return
语句之前,您无法控制任务是否完成。
因此,如果getSelectList()
在任务完成之前(并且在调用任务的return
处理程序之前)到达其onSucceeded
语句,则getSelectList()
将返回值{在任务更新之前{1}},即它将返回list
。这几乎肯定更有可能发生(因为任务正在访问数据库,这很慢),我希望这就是你得到空指针异常的原因。
如果任务完成并且在null
到达其onSucceeded
语句之前完成其getSelectList()
处理程序的调用,那么到{{} 1}}到达return
语句,getSelectList()
将被更新,并返回任务设置的值。这是极不可能的,即使它发生了,仍然没有实际保证你得到return
的“实时”值(由于Java语言规范中关于线程和内存之间关系的一些复杂性)。
请注意,如果您从FX应用程序主题调用list
,那么您保证它将返回list
,因为getSelectList()
处理程序不可能在null
完成之前调用(因为这两个方法调用都在同一个线程上运行--FX应用程序线程)。
如何修复
首先,除非进行适当的同步,否则应避免从不同的线程访问共享变量(onSucceeded
)。同步自己很困难,您通常应该使用高级API来为您管理。 getSelectList()
API专为此设计(以及常规list
API)。
我通常避免在数据访问对象类中管理线程(或异常处理),只是让客户端代码在需要时将线程代码中的DAO调用包装起来。
所以(我不打算使用这个方法Task
,因为这通常很糟糕):
java.util.concurrent
然后,从您的JavaFX UI代码中,您可以执行
static
如果您确实希望DAO方法异步运行,则需要为其提供回调,以便在成功或失败时执行。所以:
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 ;
}
}
现在,您可以通过UI执行以下操作:
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);
}
}
,而不是
原型,用于安全类型。Executor
而不是自己管理线程创建。警告所有代码都是按原样键入的,没有经过测试,因此可能存在拼写错误。它应该给你这个想法。