如何实现一个真正的异步Java线程

21

我有一个函数需要执行两个操作,其中一个快速完成,另一个需要较长时间才能运行。我想将长时间运行的操作委托给一个线程,我不关心线程何时完成,但线程必须完成。我按照下面的方式实现了这个功能,但是,我的第二个操作永远不会完成,因为函数在start()调用后退出了。我该如何确保函数返回,但第二个操作线程也完成其执行,并且不依赖于父线程?

public void someFunction(String data)
{
   smallOperation()
   SecondOperation a = new SecondOperation();
   Thread th = new Thread(a);
   th.Start();
}

class SecondOperation implements Runnable
{
  public void run(){
  // doSomething long running
 }
} 

2
你在someFunction()中从未使用过SecondOperation,你怎么期望它能运行呢? - Joachim Sauer
1
你甚至都缺少一个分号 :-/ - fortran
抱歉,我已修复了代码中的错误。 - Ritesh M Nayak
7个回答

48
public void someFunction(final String data) {
    shortOperation(data);
    new Thread(new Runnable() {
        public void run(){
            longOperation(data);
        }
    }).start();
}
如果调用了someFunction,则JVM将运行longOperation,条件是:
  1. 运行它的线程没有被标记为daemon(在上述代码中没有被标记)
  2. longOperation()不会抛出异常
  3. longOperation()没有调用System.exit()

1

从Java 1.8开始,您可以非常简单地运行异步代码。请参见下一个代码:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Semaphore;

public class HelloWorld {
  public static void main(String[] args) {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(()->
      //run the code async        
      "result"
    );
    String result = future.join(); //wait async result
    System.out.println(result);
  }
}

一个重要的侧面说明是,鉴于CompleteableFuture.cancel方法的大多数默认实现,您将永远无法中断该异步线程。 - Robert

1
这个问题可能有点晚了,但是这里有一个可行的解决方案。只需将可运行对象添加到ExecutorService中即可。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

ExecutorService executor = Executors.newFixedThreadPool(10);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                //your code
            }
        });

1
JVM在线程终止之前不会退出。你发布的代码甚至无法编译;也许问题出在你的实际代码中。

我不清楚你所说的“逻辑部分”是什么意思,但是你提供的代码并不是导致线程终止的部分。 - danben

1

如果你的第二个函数没有完成,那么它与你的函数返回无关。如果某些东西调用了System.exit()或者你的函数抛出异常,那么线程将停止。否则,即使主线程停止,它也会一直运行直到完成。这可以通过将新线程设置为守护进程来防止,但是在这里你没有这样做。


只是为了明确,将其设置为守护进程会导致JVM在没有其他非守护线程运行的情况下在完成之前退出。因此,这与您想要的相反。您可以通过在调用start()之前在Thread对象上调用setDaemon(true)来设置它。 - Yishai

0

如果我理解你的问题正确,你想调用someFunction(),执行短小的smallOperation(),运行一个线程到SecondOperation并确保从此函数返回时,SecondOperation已经完成。

如果我建议的流程是正确的,你只需要添加以下一行:

public void someFunction(String data)
{
   smallOperation()
   SecondOperation a = new SecondOperation();
   Thread th = new Thread(a);
   th.Start();
   th.join(); //add this line to make your MainThread await for this to finish . 
}

class SecondOperation implements Runnable
{
  public void run(){
  // doSomething long running
 }
} 

看一下这个文章,它说:“当我们在线程上调用join()方法时,调用线程进入等待状态。它保持在等待状态,直到引用的线程终止。” 我认为这是你想要实现的。

如果不是这种情况,而你想在SecondOperation终止时得到通知,我建议你使用asyncTask


-1

使用现代的Java解决方案

public void someFunction(String data) {
    smallOperation();
    new Thread(() -> {
        // doSomething long running
    }).start();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接