扫描仪(System.in) – 如何取消/跳过输入等待
我只是想知道如何在单独的线程中控制控制台输入?
我有线程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(); } ...
线程类似于(重写的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 (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?
谢谢
使用线程类同步线程
-
Thread.interrupt()本身不会同步两个线程之间的逻辑和时序 。
Thread.interrupt()表示调用者希望线程在不久的将来一次中断。 interrupt()方法设置一个中断标志。 isInterrupted()方法检查是否设置了此标志(并且还会再次清除该标志)。 抛出InterruptedException时,方法Thread.sleep(),Thread.join(),Object.wait()和许多I / O方法也检查并清除此标志。
线程不会立即暂停,而是继续运行代码。 内部线程逻辑由开发人员设计和实现:继续运行被认为是primefaces/紧急的线程代码,直到它达到“可中断点”,然后检查中断的标志/捕获InterruptedException然后执行干净的暂停 – 通常是通过Thread。 sleep() , Thread.join()或Object.wait() ,有时通过完全退出Thread.run()从而永久停止线程。
虽然所有这一切都在发生,但调用线程仍在运行,并且在中断生效之前将执行不确定数量的代码…因此缺乏同步。 在一个线程中的代码和另一个线程中的代码之间缺乏保证发生在之前的条件 。
-
一些方法可以在两个线程之间同步逻辑和时序(创建一个先发生的条件 ):
-
thread1调用Thread2.join()
-
thread1调用SomeObject.wait()和thread2调用SomeObject.notify()
-
在方法或块上同步
-
快速审查您的代码:
- 线程B在无限循环中运行 – 没有调用从任何线程中断它并且没有调用它的线程等待()。 但是,它会暂时阻塞,直到System.in有更多输入,然后继续。
- 线程A只会中断自身 – 如果不调用
this.interrupt()
和while(!this.isInterrupted())
清晰,更容易分析逻辑:只需将while循环更改为:do { .... } while (i != j)
- 线程A只会中断自身 – 如果不调用
this.interrupt()
和while(!this.isInterrupted())
清晰,更容易分析逻辑:只需将while循环更改为:do { .... } while (!"e".equals(command))
-
线程C必须在其循环的顶部进行以下调用:
threadB.interrupt(); synchronized(this) { try { this.wait(); } catch (InterruptedException ie) { }
-
线程B必须将以下调用作为最后一行代码:
synchronized(threadC) { threadC.notify(); }
-
从I / O读取(
nextLine()
)是一种阻塞和可中断操作。 在它旁边你引入了Thread.sleep()
,它也是一个阻塞和可中断的操作,在你的代码中引入了一个人为的延迟 – 这是没有必要的; 去掉。 - 您调用的唯一Scanner方法是
nextLine()
。 您正在使用它,就好像它是一个InputStreamReader并且没有进行任何扫描。 此外,你不是缓冲输入。 如果代码保持这样,请将’Scanner scanIn = Scanner(System.in)
‘替换为:’BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
‘。 - 您调用的唯一
ThreadGroup
方法是add()
和get()
。 您正在使用它,就好像它是一个HashMap
而没有进行任何线程组管理。 如果代码保持这样,您可以将’ThreadGroup
‘替换为’HashMap
‘。 但是,即使HashMap看起来过多 – 可以使用构造函数/ setter简单地将Threads引用传递给其他Thread,并完全避免使用HashMap。 - 避免过度使用
continue
内部循环 – 尽量避免完全避免。 最好通过使用’} else if {
‘if
‘语句链接在一起,} else if {
‘… - 主线程和线程B之间的潜在竞争条件。当线程B启动时(从
Main()
)它可以在主线程执行更多代码之前执行许多代码行–B可以在主线程调用之前调用ThreadGroup.get() ThreadGroup.add()x 3.解决方案:在Main()中,在ThreadGroup.add()x 3之后放入b.start() - 通常,
"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(); } } } } }
nextLine()
不响应中断。 你想做点什么
String command; if (scanIn.hasNextLine()) command = scanIn.nextLine(); else Thread.sleep(1000);
您可以使用标志变量(作为全局变量)来控制每个线程中的while loop
…
假设线程A有这样的无限循环
while(true) while(x == 1){ your code ... } Thread.sleep(2000); }
当线程b启动时,您可以将x更改为0(假设x是全局变量),然后当线程b在线程b代码结束时执行将更改x更改为1时…
或者你可以根据标志值x
从线程本身中断线程