1

我只是想知道如何在单独的线程中控制控制台输入?
我有线程 A 和线程 B 和线程 C;B 和 C 他们都控制用户输入......问题是我不太确定如何在 B 和 C 线程之间切换,scanIn.nextLine();因为 B 似乎在线程 C 可以中断 B 之前循环了两次不必要的迭代 :(

主线程:

  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(); 
}
...

线程 类似(被覆盖的运行方法调用)的代码:

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 (run 方法调用)

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?

谢谢

4

3 回答 3

2

使用线程类同步线程

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

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

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

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

  2. 一些在两个线程之间同步逻辑和时序的方法创建先发生条件):

    • thread1 调用 Thread2.join()

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

    • 在方法或块上同步

快速查看您的代码:

  1. 线程 B 在无限循环中运行 - 没有任何线程调用中断它,也没有调用它的线程等待()。但是,它将暂时阻塞,直到 System.in 有更多输入,然后继续。
  2. 线程 A 只会中断自身 - 如果您不调用this.interrupt()and ,则更易于分析逻辑while(!this.isInterrupted()):只需将 while 循环更改为: do { .... } while (i != j)
  3. 线程 A 只会中断自己 - 如果您不调用this.interrupt()and ,则更清晰且更容易分析逻辑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()是一个阻塞和可中断的操作。在它旁边你介绍了Thread.sleep()这也是一个阻塞和可中断的操作,它会在你的代码中引入人为的延迟——这不是必需的;消除。

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

建议更改:

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();
                }
            }
        }        
    }
}
于 2013-04-17T04:52:11.557 回答
1

nextLine()不响应中断。你想做类似的事情

String command;
if (scanIn.hasNextLine())
    command = scanIn.nextLine();
else
    Thread.sleep(1000);
于 2013-04-11T20:34:05.353 回答
0

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

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

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

当线程 b 启动时,您可以将 x 更改为 0(假设 x 是一个全局变量),然后当线程 b 完成执行时,在线程 b 代码的末尾将 x 更改为 1...

或者您可以根据标志值从线程本身中断线程x

于 2013-04-21T22:27:09.400 回答