Java:基本多线程编码

标签 java multithreading wait notify

我正在用 Java 编写一个节奏游戏;现在我已经达到了尝试实现节拍器对象的地步。
我编写了一个数据结构,将 8 个 channel 的音乐数据存储到单个 QuarterBeat 对象中;这些依次以 64 个为一组进行存储,以形成 4 个小节的“ block ”对象。
为了保持正确同步,我想使用一对并行线程:一个在停止“wait()”方法之前运行每四分之一拍发生的各种事件,而另一个则等待从在发出第一个信号之前的 BPM。

这是执行工作的线程的代码。

public class InGame {
    public static boolean gameRunning = false;
    public static boolean holdChunk = false;
    public static boolean waiting = false;
    public static ArrayList<Player> players = new ArrayList<Player>();

    public void startUp() throws InterruptedException{
        Parser.loadSamples();
        for (int p = 0; p < Player.voicePool.size(); p++) {
            Player makePlay = new Player();
            makePlay.setChannel(p);
            players.add(makePlay);
        }
        LevelStructure.SongBuild();
        Metro timer = new Metro();
        gamePlay(timer);
        gameEnd();
    }

    synchronized public void cycle(Metro timer) throws InterruptedException{
        int endPoint = LevelStructure.getChunkTotal();
        for (int chunk = 0; chunk < endPoint; chunk++){
            LevelStructure.setActiveChunk(chunk);
            for (int quartBeat = 0; quartBeat < 64; quartBeat++){
                synchronized (this){
                    new Thread(timer.ticking(this));
                    Player.getNewNotes(LevelStructure.getQuartBeat(quartBeat));
                    players.get(0).playback(LevelStructure.getQuartBeat(quartBeat));
                    waiting = true;
                    while (waiting) {
                        wait();
                    }
                }   
            }
            if (holdChunk) chunk--;
        }
    }
}

以及 Metro 对象的代码:

public class Metro {
    public static int BPM;

    synchronized public Runnable ticking(InGame parent) throws InterruptedException{
        synchronized (parent) {
            Thread.sleep(15000/BPM);
            InGame.waiting = false;
            parent.notifyAll();
        }
        return null;
    }
}

现在,每次我尝试运行它时,它都会抛出非法监视器状态异常;我尝试过自己研究 wait()/notify() 的正确实现,但我对 Java 还很陌生,我找不到我能理解的处理并行线程的解释。我是否需要从父进程调用 Cycle 和 Metro 线程?

编辑:更新代码:现在的问题是,Cycle 对象不是实际并行运行,而是等待 timer.ticking 方法执行,然后在 Metro sleep 时执行它应该执行的操作,然后卡住等待一个永远不会到来的通知。这意味着线程实际上并不是彼此并行执行的。

最佳答案

Kennedy's答案给出了您提出的有关异常的问题的解决方案,但更大的问题是您仅在一个线程中使用 wait()/notify() 。应该调用 playback() 的线程是执行 ticking() 的线程,因此它将暂停四分之一拍。我会像尝试按计划进行循环:

void cycle() 
  throws InterruptedException 
{
  int endPoint = LevelStructure.getChunkTotal();
  for (int chunk = 0; chunk < endPoint; chunk++) {
    LevelStructure.setActiveChunk(chunk);
    for (int quartBeat = 0; quartBeat < 64; quartBeat++) {
      long start = System.nanoTime();
      Player.playback(LevelStructure.getQuartBeat(quartBeat));
      long delay = 15_000_000_000L / BPM - (System.nanoTime() - start);
      if (delay < 0)
        throw new IllegalStateException("Allotted time exceeded");
      if (delay > 0)
        Thread.sleep(delay / 1_000_000, delay % 1_000_000);
    }
  }
}

如果您觉得需要多个线程,这里有一个更复杂的方法。从本质上讲,它依赖于 CyclicBarrier协调 worker 和计时器之间的关系。您还可以使用 ScheduledExecutorService 将消息安排到合成器.

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;

class Test
{

  private final CyclicBarrier sync = new CyclicBarrier(2);

  /** Duration of quarter beat in milliseconds. */
  private final int qb;

  Test(int bpm)
  {
    qb = 15000 / bpm;
  }

  private void cycle()
    throws InterruptedException, BrokenBarrierException
  {
    Timer timer = new Timer();
    timer.start();
    sync.await();
    for (int chunk = 0; chunk < 4; ++chunk)
      chunk();
    timer.interrupt();
  }

  private void chunk()
    throws InterruptedException, BrokenBarrierException
  {
    long t1 = System.nanoTime();
    for (int count = 0; count < 64; ++count) {
      playback();
      long t2 = System.nanoTime();
      sync.await(); /* Now wait the remaining time, if any. */
      long t3 = System.nanoTime();
      float t = TimeUnit.NANOSECONDS.toMillis(t2 - t1) / 1000F;
      float c = TimeUnit.NANOSECONDS.toMillis(t3 - t1) / 1000F;
      System.out.printf("Task: %5.3f, Cycle: %5.3f seconds%n", t, c);
      t1 = t3;
    }
  }

  void playback()
  {
    /* Simulate performing some work sleeping a random time which is, 
     * on average, an eighth of a beat, but can "jank" occasionally by taking 
     * too long to do its job. */
    long delay = (long) (-qb / 2 * Math.log(1 - Math.random()));
    // long delay = qb / 2; /* Simulate a predictable workload. */
    try {
      Thread.sleep(delay);
    }
    catch (InterruptedException abort) {
      Thread.currentThread().interrupt();
    }
  }

  private final class Timer
    extends Thread
  {

    @Override
    public void run()
    {
      try {
        sync.await();
        while (true) {
          Thread.sleep(qb);
          sync.await();
        }
      }
      catch (InterruptedException abort) {
        return;
      }
      catch (BrokenBarrierException ex) {
        ex.printStackTrace();
      }
    }

  };

  public static void main(String... argv)
    throws Exception
  {
    Test player = new Test(120);
    long t0 = System.nanoTime();
    player.cycle();
    System.out.printf("Total: %5.3f seconds%n", TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t0) / 1000F);
  }

}

关于Java:基本多线程编码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28749261/

相关文章:

java - 在 OSX 中打开终端并启动 .jar 的文件?

multithreading - 是否可以仅使用 std 访问具有多个线程的不相交切片?

java - 多线程 A* 寻路在自上而下游戏中卡住

multithreading - 如何从后台线程有效地对 Delphi 6 框架或表单执行图像流预览?

c++ - 在 _USE_32BIT_TIME_T 上使用 C++11 的计时

wait - 在 ansible 中继续执行下一个任务,无需等待其他主机完成该任务

java - 如何在延时后执行某个方法?

java - 远程 hibernate 标准

JAVA - Selenium WebDriver - 断言和等待

java - Build.Gradle Error (22,0) 找不到方法 chandroid()