扫描仪(System.in) - 如何取消/跳过输入等待

时间:2013-04-11 20:25:48

标签: java multithreading io

我只是想知道如何在不同的线程中控制控制台输入? 我有线程A和线程B和线程C; B和C它们都控制用户输入...事情是我不太确定如何在B和C线程之间切换scanIn.nextLine();因为B似乎在线程C可以中断B之前循环两次不必要的迭代:( < / p>

主线程:

  public class Main
        {
            private volatile ThreadGroup threadGroup=new ThreadGroup();//contains concurrent hash map...
            private volatile TaskManager taskManager=new TaskManager(threadGroup);
            private A a=new A(threadGroup);
            private B b=new B(threadGroup,taskManager);
            private C c=new C(threadGroup);


     Main()
    {

      b.start();

      threadGroup.add(a,"A");
      threadGroup.add(b,"B");
      threadGroup.add(c,"C");
    }

    public static void main(String args[]){new Main();}

        }

TaskManager方法片段:

...
public synchronized void threadCMaybeCanBeStartedLater()
{
      this.getThreadGroup().get("A").start(); 
}
...

线程像一样的代码(重写的run方法调用)

public void loopIt()
{
   Random generator = new Random(); 
   A: while(!this.interrupted())
{
   Thread.sleep(1000);

   int i=generator.nextInt(100)+1;
   int j=generator.nextInt(100)+1;
   if(i==j){this.invokeC(); System.out.println("event : i==j");}

    }
 }

private void invokeC()
{
  if(!this.getThreadGroup().get("C").isAlive())this.getThreadGroup().get("C").start(); 
}

线程B代码如:

public void loopIt() throws InterruptedException
    {


        Scanner scanIn = new Scanner(System.in);

        B: while(!this.isInterrupted())
        {

            Thread.sleep(1000);

            String command= scanIn.nextLine();
...

         if(command.equals("a"))
        {   
            System.out.println("a was entered");
            this.getTaskManager().threadCMaybeCanBeStartedLater();//             
            continue;
        }
        if(command.equals("b"))
        {   
           System.out.println("b was entered");            
           continue;
        }
        if(command.equals("c"))
        {
            System.out.println("c was entered");
            continue;
        }
        else{System.out.println("no such command");}

    }

}

线程C (运行方法调用)

public void loopIt() throws InterruptedException
        {
            getThreadGroup().get("B").interrupt();

            Scanner scanIn = new Scanner(System.in);

            C: while(!this.isInterrupted())
            {

                Thread.sleep(1000);

                String command= scanIn.nextLine();
    ...

             if(command.equals("d"))
            {   
                System.out.println("d was entered");             
                continue;
            }
            if(command.equals("e"))
            {   
               System.out.println("e was entered");            
               this.interrupt();
               break C;
            }
            if(command.equals("f"))
            {
                System.out.println("f was entered");
                continue;
            }
            else{System.out.println("no such command");}

        }

       getThreadGroup().get("B").start();

    }

...正如你所看到的,主要代码概念(参见一个线程片段)是“你不知道何时线程C可以启动但是当它开始时你需要给它控制台“;就这样;如果它是GUI没有问题,但类似控制台的应用程序使它很成问题......

所以问题是......在这种情况下如何立即从线程C中断/重新启动线程B?

由于

3 个答案:

答案 0 :(得分:2)

使用线程类同步线程

  1. Thread.interrupt()本身不会同步逻辑&amp;两个线程之间的时间

    Thread.interrupt()表示调用者将喜欢在不久的将来一次中断的线程。 interrupt()方法设置一个中断标志。 isInterrupted()方法检查是否设置了此标志(&amp;也再次清除该标志)。方法Thread.sleep(),Thread.join(),Object.wait()和许多I / O方法也检查&amp;抛出InterruptedException时清除此标志。

    线程不会立即暂停但会继续运行代码。内部线程逻辑设计&amp;由开发人员实现:继续运行被认为是原子/紧急的线程代码,直到它达到“可中断点”,然后检查被中断的标志/捕获InterruptedException&amp;然后做一个干净的暂停 - 通常是通过 Thread.sleep() Thread.join() Object.wait(),有时通过完全退出 Thread.run(),从而永久停止线程。

    虽然所有这一切都在发生,但调用线程仍在运行,并且在中断生效之前将执行不确定数量的代码...因此缺乏同步。在一个线程中的代码和另一个线程中的代码之间缺少有保证的发生前的条件

  2. 的一些方法同步逻辑&amp;两个线程之间的时间间隔(创建发生前的条件):

    • thread1调用Thread2.join()

    • thread1调用SomeObject.wait()和thread2调用SomeObject.notify()

    • 对方法或块进行同步

  3. 快速审核您的代码:

    1. 线程B在无限循环中运行 - 没有调用从任何线程中断它并且没有调用它的线程等待()。但是,它会暂时阻塞,直到System.in有更多输入,然后继续。
    2. 线程A只会中断自身 - 如果您不致电this.interrupt()while(!this.isInterrupted()),则更清晰,更容易分析逻辑:只需将while循环更改为:do { .... } while (i != j)
    3. 线程A仅中断自身 - 如果您不致电this.interrupt()while(!this.isInterrupted()),则更清晰,更容易分析逻辑:只需将while循环更改为:do { .... } while (!"e".equals(command))
    4. 线程C必须在while循环的顶部进行以下调用:

       threadB.interrupt();
       synchronized(this) {
           try {
               this.wait();
           } catch (InterruptedException ie) {
           }
      
    5. 线程B必须将以下调用作为最后一行代码:

       synchronized(threadC) {
               threadC.notify();
       }
      
    6. 从I / O(nextLine())读取是一个阻止&amp;可中断的操作。在它旁边你引入了Thread.sleep(),这也是一个阻塞&amp;可中断操作会在代码中引入人为延迟 - 这是不必要的;除去。

    7. 您调用的唯一扫描程序方法是nextLine()。你正在使用它就好像它是一个InputStreamReader&amp;没有做任何扫描。此外,你不是缓冲输入。如果代码保持这样,请将“Scanner scanIn = Scanner(System.in)”替换为:“BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))”。
    8. 您调用的唯一ThreadGroup方法是add()get()。您正在使用它,就好像它是HashMap&amp;没有做任何线程组管理。如果代码保持这样,您可以将“ThreadGroup”替换为“HashMap”。但是,即使HashMap看起来过多 - 可以使用构造函数/ setter简单地将Threads引用传递给其他Thread,并完全避免使用HashMap。
    9. 避免过度使用continue内部循环 - 尽量避免使用。最好通过使用'if'链接连续的'} else if {'语句...
    10. 主线程和线程B之间的潜在竞争条件。当线程B启动时(来自Main()),它可以在主线程执行更多代码之前执行多行代码--B可以调用ThreadGroup.get()在主线程调用ThreadGroup.add()x 3之前解决方案:在Main()中,在ThreadGroup.add()x 3之后放入b.start()
    11. 一般来说,"a".equals(command)command.equals("a")更好的做法 - 它处理空值,在没有NPE的情况下给出正确的结果(这里你似乎很幸运 - 可能没有空值)。
    12. 建议更改:

      public class ThreadA extends Thread {
      
          ThreadC threadC;
      
          public void setThreadC(ThreadC threadC) {
              this.threadC = threadC;
          }
      
          @Override
          public void run() {
              this.loopIt();
          }
      
          public void loopIt() {
              Random generator = new Random(); 
              int i, j;
              do {
                  try { 
                      Thread.sleep(1000);
                  } catch (InterruptedException ie) {                
                  }
                  i=generator.nextInt(100)+1;
                  j=generator.nextInt(100)+1;
              } while (i != j);
              threadC.start();
          }
      
      }
      public class ThreadB extends Thread {
      
          ThreadA threadA;
          ThreadC threadC;
      
          public void setThreadA(ThreadA threadA) {
              this.threadA = threadA;
          }
          public void setThreadC(ThreadC threadC) {
              this.threadC = threadC;
          }
      
          @Override
          public void run() {
              this.loopIt();
          }
      
          public void loopIt() {
              BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
              String command = null;
              // loop until interrupted
              try {
                  while (!this.isInterrupted()) {
                      command = reader.readLine();
                      if ("a".equals(command)) {   
                          System.out.println("a was entered");
                          if (threadA.getState() == Thread.State.NEW) {
                              threadA.start();
                          }
                      } else if ("b".equals(command)) {   
                          System.out.println("b was entered");            
                      } else if ("c".equals(command)) {
                          System.out.println("c was entered");
                      } else if ("z".equals(command)) {
                          System.out.println("z was entered");
                          throw new InterruptedException("Command z interruption");
                      } else {
                          System.out.println("no such command");
                      }
                  }
              } catch (IOException ioe) {
                  ioe.printStackTrace();
              } catch (InterruptedException ie) {
              }
              // Now notify ThreadC - it will wait() until this code is run
              synchronized(threadC) {
                  threadC.notify();
              }
          }
      }
      
      public class ThreadC extends Thread {
      
          ThreadB threadB;
      
          public void setThreadB(ThreadB threadB) {
              this.threadB = threadB;
          }
      
          @Override
          public void run() {
                  this.loopIt();
         }
      
          public void loopIt() {
              // Block until the lock can be obtained
              // We want thread B to run first, so the lock should be passed into Thread C constructor in an already locked state
              threadB.interrupt();
              synchronized(this) {
                  try {
                      // Put this thread to sleep until threadB calls threadC.notify().
                      //
                      // Note: could replace this line with threadB.join() - and remove  
                      // from threadB the call to threadC.notify()
                      this.wait();
                  } catch (InterruptedException ie) {
                  }
                  BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                  String command = null;
                  while (!"e".equals(command)) {
                      try {
                          command= reader.readLine();
                          if ("d".equals(command)) {   
                              System.out.println("d was entered");             
                          } else if ("e".equals(command)) {    
                              System.out.println("e was entered");            
                          } else if ("f".equals(command)) {
                              System.out.println("f was entered");
                          } else if ("z".equals("command")) {
                              System.out.println("z was entered");
                          } else { 
                              System.out.println("no such command");
                          };
                      } catch (IOException ioe) {
                          ioe.printStackTrace();
                      }
                  }
              }        
          }
      }
      

答案 1 :(得分:1)

nextLine()没有响应中断。你想做类似

的事情
String command;
if (scanIn.hasNextLine())
    command = scanIn.nextLine();
else
    Thread.sleep(1000);

答案 2 :(得分:0)

您可以使用标志变量(作为全局变量)来控制每个线程中的while loop ...

假设线程A具有像这样的无限循环

while(true)
 while(x == 1){
   your code ...
 }
  Thread.sleep(2000);
}

当线程b启动时,您可以将x更改为0(假设x是全局变量),然后当线程b在线程b代码结束时执行将x更改为1时...

或者你可以根据标志值x

从线程本身中断线程