Java: 编码基本多线程
Java: coding basic multithreading
我在Java写节奏游戏;现在我已经到了尝试实现节拍器对象的地步。
我编写了一个数据结构,将 8 个通道的音乐数据存储到一个 QuarterBeat 对象中;这些依次存储在 64 组中,以制作 4-measure 'chunk' 对象。
为了保持同步,我想使用一对并行线程:一个 运行s 处理每四分之一拍发生的各种事件,然后停止在 '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 休眠时执行,然后卡住等待永远不会到来的通知。这意味着线程实际上并没有彼此并行执行。
我猜这是因为你在调用 this.wait()
时没有锁定对象 this
。
先尝试获取锁。
synchronized (this) {
this.wait();
}
答案给出了您提出的有关异常的问题的解决方案,但更大的问题是您仅在一个线程中使用 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
to coordinate between the worker and the timer. You could also schedule messages to the synthesizer using a 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写节奏游戏;现在我已经到了尝试实现节拍器对象的地步。
我编写了一个数据结构,将 8 个通道的音乐数据存储到一个 QuarterBeat 对象中;这些依次存储在 64 组中,以制作 4-measure 'chunk' 对象。
为了保持同步,我想使用一对并行线程:一个 运行s 处理每四分之一拍发生的各种事件,然后停止在 '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 休眠时执行,然后卡住等待永远不会到来的通知。这意味着线程实际上并没有彼此并行执行。
我猜这是因为你在调用 this.wait()
时没有锁定对象 this
。
先尝试获取锁。
synchronized (this) {
this.wait();
}
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
to coordinate between the worker and the timer. You could also schedule messages to the synthesizer using a 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);
}
}