【问题标题】:Run multiple java thread sequentially with executer Services使用执行器服务顺序运行多个 java 线程
【发布时间】:2021-07-08 04:36:30
【问题描述】:

我想依次运行 2 个或更多线程,我的意思是例如:首先它应该运行第一个线程,然后运行第二个线程,然后......。 我用过 Executors.newSingleThreadExecutor();也。我有 3 个任务:1-创建一个文件 2- 在其中写一些东西 3- 读取文件 创建任务:

 public class FirstTask implements Runnable{
 private CreateRoleFile createFiel = new CreateRoleFile();
  @Override
  public void run() {
    createFiel.createFile();
}

}

createFile() 方法:

    public Path createFile(){

    Path path = Paths.get("Files/first.txt");

    if (!Files.exists(path)) {
        try {
            Files.createFile(path);

        } catch (IOException e) {
            System.out.println("something went wrong while creating first.txt .Please try again!");
        }
        System.out.println("thread name = "+Thread.currentThread().getName());
        return path;
    } else {
        System.out.println("This file is already exist!!");
        System.out.println("thread name = "+Thread.currentThread().getName());
        return path;
    }
}

SecondTask 类是:

public class SecondTask implements Runnable {
WriteRoleFile writeFile = new WriteRoleFile();

@Override
public void run() {
    writeFile.Writefile("1020");
}

} 这是我的主要方法:

 public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService executorService1 = Executors.newSingleThreadExecutor();
    Runnable firstTask =new FirstTask();
    executorService1.execute(firstTask);
    executorService1.shutdown();

   ExecutorService executorService2=Executors.newSingleThreadExecutor();
   Runnable secondTask = new SecondTask();
   executorService2.submit(secondTask);
    executorService2.shutdown();

   ExecutorService executorService3 =Executors.newSingleThreadExecutor();
    Callable thirdTask=new ThirdTask();
    executorService3.submit(thirdTask);
    executorService3.shutdown();
}

ThirdTask 类是:

public class ThirdTask implements Callable<String> {
ReadRoleFile readeer = new ReadRoleFile();

@Override
public String call() {
    String s = readeer.readFile();
    return s;
}

}

readFile() 方法是:

public String readFile() {
    Path path = Paths.get("Files/first.txt");
    String s = "";
    try {
        if (Files.size(path) == 0) {
            System.out.println("nothing has been wrote yet .");
        } else {
            try {
                BufferedReader bufferedReader = Files.newBufferedReader(path);
                s = bufferedReader.readLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    System.out.println("thread name = " + Thread.currentThread().getName());
    System.out.println(s);
   return s;
}

输出是: 这个文件已经存在!! 线程名称 = pool-1-thread-1 线程名称 = pool-3-thread-1 空值 线程名称 = pool-2-thread-1

**我需要先运行 pool-1-thread-1 和 pool-2-thread-1 因为它必须先在文件中写入一个数字,然后 pool-3-thread1 才能从文件中读取 * *

【问题讨论】:

  • 很高兴看到您参与 Stack Overflow。请用适当的大小写、标点符号等来写你的散文。这个网站更像维基百科,而不是一个休闲聊天室。

标签: java multithreading threadpool


【解决方案1】:

保留一个执行器服务,以供后续使用

执行器服务由一个或多个线程池支持。执行者服务的目的是:

  • 管理这些线程的创建、到期和调度
  • 将您的任务(您的RunnableCallable 对象)分配给这些线程以供执行。

所以请保留您的 ExecutorService 对象。您正在创建新的,然后将它们关闭。如果要连续运行三个任务,请仅使用单个执行器服务。

ExecutorService es = Executors.newSingleThreadExecutor();

Runnable task1 = new FirstTask();
es.execute( task1 );

Runnable task2 = new SecondTask();
es.execute( task2 );

Runnable task3 = new ThirdTask();
es.execute( task3 );

es.shutdown();              // Disallow any more tasks to be submitted.
es.awaitTermination( … ) ;  // Wait for submitted tasks to be done/canceled/failed.

您的多个执行器服务对象出现看似疯狂的行为的原因是,当 CPU 内核上的哪个执行器服务获得多少执行时间是不可预测的。您的第二个 executor 服务可能首先启动,但在其工作中途被暂停。同时,第三个执行器服务可以开始和完成它的工作,甚至在第一个执行器服务开始它的工作之前。

每次运行应用程序时,先到后三的顺序以及完成的顺序都会有所不同。 CPU 内核上的任务调度由 JVM 和主机操作系统随心所欲地完成,并随运行时的瞬时条件而变化。

如果您希望在单个后台线程上按顺序完成一系列任务,请使用单个 newSingleThreadExecutor() 对象来分配所有这些任务。 p>

【讨论】:

  • 非常感谢您的完整解释。我完全理解我的严重错误;))
  • @Basil Bourque 要从 Future 获取结果,可以使用 ExecutorService 通过 .get(); 传递结果;方法。但是该方法将阻塞,直到它获取数据。那么你有没有推荐过使用 CompletableFuture 和 .supplyAsync() 和 thenApply() 来获得结果而不阻塞?如果是这样,在这种情况下我还需要为 CompletableFuture 设置一个 Callable 还是只设置一个 CompletableFuture?
  • @AJW 如果等待一堆任务全部完成,请收集提交给执行器服务时返回的Future 对象。然后在服务关闭完成后,循环期货集合以查看是否完成、取消或失败。如果你想要返回值,那么可以使用Callable 而不是Runnable。如果您想要一系列任务,那么是的CompletableFuture。但仅供参考,在未来,Project Loom 技术可能会消除 CompletableFuture 的许多方法和用法中的大部分。请参阅 Ron Pressler 和其他在 Loom 上工作的 Oracle 员工的谈话。
  • @Basil Bourque 谢谢。但我的任务非常简单,例如将少量数据从 CardView 插入 Room 数据库,或更新该数据,或删除该数据或从 Room 请求所有数据以在 CardViews 的 RecyclerView 中显示列表。随着时间的推移,卡片视图列表可能会增长,如果 UI 被阻止,我绝对不想使用 .get() 。这就是为什么我将 CompletableFuture 视为一种潜在的解决方案,因为它不会阻塞。我正在尝试替换现有的 AsyncTask(),因为该方法已被弃用。非常感谢您的想法或想法。
  • @AJW 所有这些都已经在 Stack Overflow 上讨论过很多次了。搜索以查找更多信息。
【解决方案2】:

这可能不是您问题的准确答案。
我们可以使用如下所示的标志来控制线程的执行。
执行顺序 Thread1 --> Thread2 --> Thread3 always.

import com.google.common.collect.Lists;

import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

AtomicBoolean flag1 = new AtomicBoolean(false); // to indicate Thread1 completed execution
AtomicBoolean flag2 = new AtomicBoolean(false); // to indicate Thread2 completed execution
final CopyOnWriteArrayList<String> result = new CopyOnWriteArrayList<>(); // thread safe collection just to capture results from threads

Callable<Void> r1 = () -> {
    result.add("Thread1 : " + Thread.currentThread().getName());
    flag1.set(true);
    return null;
};

Callable<Void> r2 = () -> {
    while (!flag1.get()) {
        System.out.println("thread2 waiting");
    }
    result.add("Thread2 : " + Thread.currentThread().getName());
    flag2.set(true);
    return null;
};

Callable<Void> r3 = () -> {
    while (!flag2.get()) {
        System.out.println("thread3 waiting");
    }
    result.add("Thread3 : " + Thread.currentThread().getName());
    return null;
};

// using same executor service for all the threads as suggested from this answer: https://stackoverflow.com/a/67069903/2987755
ExecutorService ec = Executors.newCachedThreadPool();

// call all the threads
ec.invokeAll(Lists.newArrayList(r1, r2, r3));

// we do not need ExecutorService anymore so shutting it down
ec.shutdown();

// print results
System.out.println(result);


//output
// truncated system out waiting log
//[Thread1 : pool-1-thread-1, Thread2 : pool-1-thread-2, Thread3 : pool-1-thread-3]

【讨论】:

  • flag1.get() 是非阻塞的,您的 while 循环将不间断地执行。您可以使用 wait()/notify() 或更好的选项 java.util.concurrent.locks.Lock 类。
【解决方案3】:

当您按顺序执行三个任务时,您只需要一个ExecutorService 和一个thread。提交所有任务,等待结果:

String fileName = "e:\\temp.txt";
Callable<Path> createTask = ()->{System.out.println("File created"); return Path.of(fileName);}; 
Runnable writeTask = ()->{System.out.println("Written to the file"); /*put write code here*/};
Callable<String> readTask = ()->{System.out.println("Reading from the file"); return "fileData 1020";};

ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(createTask);
es.execute(writeTask);
Future<String> f = es.submit(readTask);

es.shutdown();
String data = f.get(); //wait for readTask to finish then get data
System.out.println("Read Data is: " + data);

输出:

File created
Written to the file
Reading from the file
Read Data is: fileData 1020

这里我使用 lambdas 来定义任务以保持简单。
ExecutorService 维护所有已完成任务的队列。由于我们在此服务中只有一个线程,因此所有任务将按照先进先出的方式一个一个地执行。
此外,我们使用Future&lt;String&gt; 对象来检索文件读取结果。 Future#get() 等待任务完成,然后返回结果。

【讨论】:

  • 我有一个类似的用例,试图将 AsyncTasks 转换为 ExecutorService 以从 Room 数据库返回一个列表。另一个答案建议在 ExecutorService 上使用 submit(),在 Dao 方法上使用 get()(请参阅 stackoverflow.com/questions/52242933/room-database-query)。将不胜感激您对该方法与上述使用 Callable 和 Future 的方法的想法。
  • 实际上,他并没有在 Dao 上调用 get()。他也和我在这里做同样的事情。只是他直接在submit()方法返回的future对象上调用了get()。他没有将未来对象存储在任何变量中。此外,如果您不想阻止 get() 方法,请尝试此stackoverflow.com/q/35366018/15273968。或使用CompletableFuture.supplyAsync(()-&gt;{return Test.task();}) .thenApply((s)-&gt; {System.out.println("Task output: " +s);return "";});
  • 所以在他的例子中,future 对象是字符串“名称”?感谢您的非阻塞推荐,我会审查。
  • 追问:在使用CompletableFuture的时候还需要先设置Call​​able吗?
猜你喜欢
  • 2020-05-18
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 1970-01-01
  • 2018-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多