gpt4 book ai didi

java - 无法在静态方法中将 JavaFx 任务委托(delegate)给新线程

转载 作者:行者123 更新时间:2023-12-01 22:22:09 25 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





What is the difference between Thread.start() and Thread.run()?

(9 个回答)


6年前关闭。




我有一个 General 类,它具有静态方法和静态变量,具有 Hibernate 配置设置和在访问数据库后返回 List 的方法。我正在研究 JavaFx,最近我学会了更好地使用 Task 进行耗时的操作,比如点击 db 以获得一长串数据等。
所以,在我的例子中,我创建了一个 Task 对象,在匿名内部类中编写了代码,其中包含了为 LOGIN 凭据访问数据库的代码。 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;

}

Exception which i got

然而,在许多组合和随机排列之后,对我来说,评论线程代码而不是用 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 转换为静态并删除 Thread 实例并使用 task.run();只是我的不同尝试使我的代码工作的一个数字。我不知道原因。
对于任何解释,我都会感到非常谦卑。提前致谢!

最佳答案

发生了什么

创建 Thread完成您的任务,然后调用 start()在线程上导致task.run()在您创建的线程中异步执行。

只需调用task.run()执行 run()当前线程中的方法。

线程中的代码的行为是不确定的。换句话说,您无法仅从代码中预测结果将是什么。问题是您正在访问共享 list来自两个不同线程的实例,无法控制访问顺序。这是发生的事情:
list最初是 null .

您调用getSelectList() .
getSelectList() :

  • 您创建一个任务。
  • 您将任务配置为设置 list 的值到任务完成时的查询结果。 (这将发生在 FX 应用程序线程上。)
  • 您创建一个将执行任务的新线程。
  • 您启动线程,导致任务异步执行
  • 您返回 list 的值

  • 因为任务是异步执行的,所以您无法控制任务是否在 getSelectList() 之前完成。达到其 return陈述。

    所以如果 getSelectList()达到其 return任务完成之前的语句(以及在调用任务的 onSucceeded 处理程序之前), getSelectList()将返回 list 的值在任务更新它之前,即它将返回 null .这几乎肯定更有可能发生(因为任务正在访问数据库,这很慢),我希望这就是你得到空指针异常的原因。

    如果任务恰好完成并完成其 onSucceeded的调用 getSelectList() 之前的处理程序达到其 return声明,然后由时间 getSelectList()到达 return声明, list将被更新并返回任务设置的值。这是极不可能的,即使发生了,仍然不能保证您获得 list 的“实时”值。 (由于 Java 语言规范中关于线程和内存之间关系的一些复杂性)。

    请注意,如果您调用 getSelectList()来自 FX 应用程序线程,那么您是 保证它将返回 null , 因为 onSucceededgetSelectList() 之前不可能调用处理程序完成(因为这两个方法调用都在同一个线程上运行 - FX 应用程序线程)。

    如何修复它

    首先,您应该避免从不同的线程访问共享变量( list ),除非您有适当的同步。同步自己很困难,您通常应该使用高级 API 来为您管理。 Task API 就是为此而设计的(连同通用的 java.util.concurrent API)。

    我通常避免在数据访问对象类中管理线程(或异常处理),如果需要,只需让客户端代码将对 DAO 的调用包装在线程代码中。

    所以(我不打算使用这种方法 static 因为这通常很可怕):
    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 ;
    }
    }

    然后,从您的 JavaFX 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();

    如果您确实希望 DAO 方法异步运行,则需要为其提供回调以在其成功或失败时执行。所以:
    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);
    }
    }

    现在,从您的 UI 中,您可以执行以下操作:
    DAO myDAO = ... ;
    String query = ... ;

    myDAO.getSelectList(query,
    list -> Platform.runLater(() -> {
    // update UI with list ...
    }),
    exc -> Platform.runLater(() -> {
    // handle exception...
    })
    );

    进一步改进
  • 您应该使用正确的通用类型 List , 而不是
    原始类型,用于类型安全。
  • 使用 Executor 而不是自己管理线程创建。

  • 警告 所有代码都是按原样输入的,没有经过测试,所以可能有错别字。它应该给你的想法。

    关于java - 无法在静态方法中将 JavaFx 任务委托(delegate)给新线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29521470/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com