尝试使用信号量时,只有一个线程执行

问题描述 投票:0回答:1

据我了解,

java.util.concurrent.Semaphore
允许我指定一次可以有多少个线程使用资源。线程可以使用
Semaphore.acquireUninterruptibly()
来消耗信号量中有限数量的“槽”。线程完成后,应调用
Semaphore.release()
来归还插槽,以便另一个正在等待的线程(
acquireUninterruptibly()
使线程等待)可以获取新插槽。我知道公平政策,线程的顺序与我的目的无关。重要的是所有线程都执行。

这就是我的问题——只有一个线程执行。这是我的代码。

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
 
public class Main
{
 
   private static final Path rootbarFolder = Path.of("C:", "Users");
 
   private boolean REATTEMPT_UPON_FAILURE = true;
 
   public static void main(final String[] args) throws Exception
   {
  
      new Main();
  
   }
 
   private Main() throws Exception
   {
  
      javax.swing.SwingUtilities
         .invokeLater
         (
            () ->
            {
           
               javax.swing.JOptionPane.showMessageDialog(null, "Close this popup window to make the program exit on failure");
           
               this.REATTEMPT_UPON_FAILURE = false;
           
            }
         )
         ;
  
      final List<Path> fooPathList = this.getListOfbarfooPaths();
  
      final List<Thread> fooPathThreads = new ArrayList<>();
  
      final Semaphore semaphore = new Semaphore(1, true);
  
      KICK_OFF_THREADS:
      for (final Path fooPath : fooPathList)
      {
     
         final Thread fooPathThread = this.createThread(semaphore, fooPath);
     
         fooPathThreads.add(fooPathThread);
     
         semaphore.acquireUninterruptibly();
     
         fooPathThread.start();
        
      }
  
      JOIN_THREADS:
      for (final Thread fooPathThread : fooPathThreads)
      {
     
         fooPathThread.join();
        
      }
  
   }
 
   private Thread createThread(final Semaphore semaphore, final Path fooPath)
   {
  
      return
         Thread
            .ofPlatform()
            .unstarted
            (
               () ->
               {
              
                  try
                  {
                 
                     int exitCode = -1;
                 
                     while (exitCode != 0 && this.REATTEMPT_UPON_FAILURE)
                     {
                    
                        final Process fooProcess = this.createzilklaquo(fooPath);
                    
                        exitCode =                             //Tells us the status of the run
                           fooProcess.waitFor();           //Don't close down the JVM before this process finishes running!
                    
                        System.out.println(fooPath + " -- fooProcess exitCode = " + exitCode);
                        Thread.sleep(10_000);
                    
                     }
                 
                  }
                 
                  catch (final Exception exception)
                  {
                 
                     throw new RuntimeException(exception);
                 
                  }
                 
                  finally
                  {
                 
                     semaphore.release();
                 
                  }
              
               }
            )
            ;
  
   }
 
   private Process createzilklaquo(final Path fooPath)
   {
  
      try
      {
     
         final ProcessBuilder fooProcessBuilder =
            new
               ProcessBuilder
               (
                  "cmd",
                  "/C",
                  "THIS_COMMAND_WILL_FAIL"
               )
               .directory
               (
                  rootbarFolder              //
                     .resolve(fooPath)      //
                     .toFile()                  //
               )
               .inheritIO()
               ;
     
         fooProcessBuilder
            .environment()
            .put("SUB_FOLDER", fooPath.getFileName().toString())
            ;
     
         final Process fooProcess =
            fooProcessBuilder
               .start()                            //Kicks off the newly created Process
               ;
     
         // final int exitCode =                   //Tells us the status of the run
            // fooProcess.waitFor();           //Don't close down the JVM before this process finishes running!
      //
         // System.out.println("fooProcess exitCode = " + exitCode);
     
         return fooProcess;
     
      }
     
      catch (final Exception e)
      {
     
         throw new RuntimeException(e);
     
      }
  
   }
 
   private List<Path> getListOfbarfooPaths() throws Exception
   {
  
      final Process fooListProcess =
         new
            ProcessBuilder
            (
               "cmd",                        //Since this is Windows, CMD is the easiest way to accomplish what we want
               "/C",                         //Starts an instance of CMD, does the below commands, outputs/pipes them, then immediately closes
               "dir",                        //Lists all contents in the folder
               "/A:D",                       //Filters the contents down to only directories
               "/B"                          //Removes extra metadata -- just the names
            )
            .directory
            (
               rootbarFolder              //Perform the action in the root bar folder
                  .toFile()                  //Wish I could give a Path instead of a File
            )
            //.inheritIO()                     //Forward all Input and Output to the same as Java's (commented out because it drowns out my logs)
            .start()                         //Kicks off the newly created Process
            ;
  
      final int exitCode =                   //Tells us the status of the run
         fooListProcess.waitFor();       //Don't close down the JVM before this process finishes running!
  
      System.out.println("fooListProcess exitCode = " + exitCode);
  
      final String fooListRawOutput =    //The raw output from the newly created process
         new
            String
            (
               fooListProcess            //Now that the process has finished, we can pull from it
                  .getInputStream()          //The way that you quo the OUTPUT of the process is to call getINPUTStream -- very unintuitive
                  .readAllBytes()            //Let's quo all of it
            )
            ;
  
      final List<Path> fooList =       //The list of foos that we will be working with
         fooListRawOutput                //We will be extracting it from the raw output
            .lines()                         //It's a new-line-separated list, so split by line
            .map(rootbarFolder::resolve)  //Turn it into a Path that is the rootbarFolder resolved to the sub-folder -- root -> root/subFolder
            .toList()                        //Finally, put the contents into a list
            ;
  
      fooList.forEach(System.out::println);
  
      return fooList;
  
   }
 
}

再次强调,这是一个基于真实示例的假示例,由于公司政策,我无法展示。

我有一个文件夹,里面有很多子文件夹。我想做多线程来对这些子文件夹做一些工作。我所做的工作经常失败,所以我有一个 while 循环监听 exitCode,然后重新尝试。然而,由于我不能说的原因,我需要一个逃生舱口,上面写着“从现在开始,任何未来的失败都应该结束线程执行”。这就是

JOptionPane
的用途。我知道这是一个糟糕的例子,但重点是它切换了一个
boolean
标志,这就是我需要它做的事情。

这就是我的问题。当我切换标志时,应该等待信号量以获得空闲插槽的后续线程不会被启动。

至于上面的代码实际上在做什么,它尝试一个肯定会失败的命令,触发我提到的 while 循环机制。然后,线程休眠 10 秒,然后重试。

我有一个弹出窗口,允许我在选择按“确定”时设置标志。当我这样做时,第一个线程完成,但下一个线程永远不会启动。

现在,我知道 JVM 可以采用多线程“捷径”(因为缺乏更好的术语),这很可能就是这里发生的情况。但不知道是不是这个原因。

为什么我的其余线程没有被启动?

java concurrency semaphore java.util.concurrent java-threads
1个回答
0
投票

从您的描述和提供的代码来看,线程未按预期执行的问题似乎与在 for 循环中使用

Semaphore
启动线程有关。以下是解决问题的一些见解和建议:

  1. 信号量用法:在代码中,您在启动线程的循环(

    KICK_OFF_THREADS
    循环)内获取信号量许可。这意味着在第一个线程启动后,主线程(正在运行循环)将持有信号量,并且在许可证可用之前不会释放它。这本质上会造成死锁,因为主线程正在等待只能由它刚刚启动的线程释放的许可,但该线程无法运行,因为主线程正在阻塞。

  2. 线程启动机制:要解决此问题,您应该从启动线程的循环中删除

    semaphore.acquireUninterruptibly();
    行。相反,管理线程本身内的信号量。每个线程应该在开始时获取信号量并在结束时释放它。这确保在任何给定时间,只有有限数量的线程(根据信号量的许可)正在执行其任务。

  3. 线程执行流程:通过每个线程内部的信号量获取/释放,主线程不会阻塞,可以继续创建和启动所有线程。信号量将确保只有有限数量的线程同时运行其任务。

  4. 处理“逃生舱口”:对于“逃生舱口”功能,您可能需要确保它不会干扰信号量机制。确保切换标志时,不会导致线程提前终止而不释放其信号量许可。

通过按所述调整信号量处理,您的线程应该能够并发运行,遵守信号量设置的限制,并且不会被主线程的循环阻塞。

尝试写详细的回复,希望有帮助:)

© www.soinside.com 2019 - 2024. All rights reserved.