2016-11-13 162 views
0

基本上我想在调用方法之后暂停我的线程,然后继续执行另一个方法。我无法循环,我的方法只能运行一次。 这个想法背后,是用在游戏中,方法将显示消息,并且每当用户按下一个键时,就会显示下一条消息。我不能仅仅通过一个列表,因为游戏需要用户输入。我看着Thread.pause()Thread.resume()但他们不会工作,并已弃用。 我当前的代码(这是不工作):在调用方法后暂停线程

private Thread thread; 
private Thread managerThread; 
private final Object lock = new Object(); 

private boolean shouldThreadRun = true; 
private boolean storyRunning = true; 

public Storyline() { 
    setUpThread(); 
} 

private void setUpThread() { 
    managerThread = new Thread(() -> { 
     while(storyRunning) { 
      synchronized (lock) { 
       if(!shouldThreadRun) { 
        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
       System.out.println("Looping"); 
      } 
     } 
    }); 
    thread = new Thread(() -> { 
     synchronized (lock) { 
      pauseThread(); 
      System.out.print("A"); 
      pauseThread(); 
      System.out.print("B"); 
     } 
    }); 
    managerThread.start(); 
    thread.start(); 
} 

public void pauseThread() { 
    shouldThreadRun = false; 
} 
public void resumeThread() { 
    shouldThreadRun = true; 
} 
+0

你不会在我看到的任何地方调用'resumeThread()'。另外,如果要由不同的线程修改和读取'shouldThreadRun',则应声明'volatile'。 –

+0

https://docs.oracle.com/javase/tutorial/essential/concurrency/join.html –

+0

因为我正在测试以查看'B'是否打印过,因为它不应该打印。但它确实被打印 –

回答

0

看看我的编辑,看看它是否是任何类似于你想要的目的。我正在使用扫描仪来模拟用户输入,只需按下键盘上的Enter键即可进行尝试。顺便说一句,我希望这只是一个练习,而不是现实生活中的情况。您应该尽量避免在实际产品中出现这种低水平的多线程管理,除非确实需要,在这种情况下,您仍然应该使用适当的数据结构。在一个真正的应用程序中,按钮将被链接到回调函数,并且只要按下按钮,您就会设置一些onClick()方法来执行您需要的代码。

对于所关注的并发,我强烈建议你看看这些教程:Oracle-Concurrency

PS:请注意,我完全忽略中断,这是一个不好的做法,这些异常应处理的权利方式:我只是试图通过保持代码尽可能简单来达到预期的结果。另外,就像其他人指出的那样,您应该通过调用循环内的等待来处理虚假唤醒。

private Thread thread; 
private Thread managerThread; 
private final Object lock = new Object(); 

Scanner in; 

public Storyline() { 
    setUpThread(); 
} 

private void setUpThread() { 
    managerThread = new Thread(() -> { 
     while(true) { 
      in = new Scanner(System.in); 
      in.nextLine(); 
      resumeThread(); 
     } 
    }); 
    thread = new Thread(() -> { 
     synchronized (lock) { 
      while(true){ 
       System.out.print("A"); 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) {} 
       System.out.print("B"); 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) {} 
      } 
     } 
    }); 
    managerThread.start(); 
    thread.start(); 
} 


public void resumeThread() { 
    synchronized(lock){ 
     lock.notify(); 
    } 
} 
+0

我不知道谁低估了这个,它做到了这一招,谢谢! –

+0

我很高兴这很有帮助 – c0rtexx

0

的Object.wait,as described in the documentation的第一条规则是,它必须一个循环依赖于它是等待的基础条件来调用。

所以,你的等待需要看起来像这样:

synchronized (lock) { 
    while (!shouldThreadRun) { 
     lock.wait(); 
    } 
} 

中断是不是由事故发生。如果另一个线程明确要求它停止正在执行的操作并完全退出,则线程只会被中断。

因此,如果你得到一个中断,正确的做法是而不是忽略它并打印堆栈跟踪。你需要干净地退出。

做到这一点,最简单的方法是简单地附上整个while循环在一个try/catch:

try { 
    while (storyRunning) { 
     synchronized (lock) { 
      while (!shouldThreadRun) { 
       lock.wait(); 
      } 
      System.out.println("Looping"); 
     } 
    } 
} catch (InterruptedException e) { 
    System.out.println("Exiting, because someone asked me to stop."); 
    e.printStackTrace(); 
} 

这样,你的while循环将自动中断时退出。

最后,Object.wait是无用的,除非另一个线程在等待线程同步的同一个对象上调用Object.notify或Object.notifyAll。等待方法将(可能)永远不会返回,除非对象得到通知:

public void pauseThread() { 
    synchronized (lock) { 
     shouldThreadRun = false; 
     // Tell waiting thread that shouldThreadRun may have changed. 
     lock.notify(); 
    } 
} 

public void resumeThread() { 
    synchronized (lock) { 
     shouldThreadRun = true; 
     // Tell waiting thread that shouldThreadRun may have changed. 
     lock.notify(); 
    } 
} 

请注意,同步是在方法内。如果您始终保持线程同步于lock,那么管理器线程永远不会有机会运行,因为它试图获取同一对象上的同步锁。(然而,相反的是不正确的;所述管理器线程可以保持上lock所有的时间同步,因为等待()方法将暂时释放同步锁,从而允许另一个线程进行。)

如果所有访问shouldThreadRun的代码位于同步块内部,因为同步已确保多线程一致性,所以您不需要(也不应该)使shouldThreadRun易变。