How to terminate anonymous threads in java - java

This battleship game is the first multi-threaded application I've tried to write and it worked perfectly up until I added the multi-threading,which is only used for sound effects. It is all in one single class, the AudioManager.
I'm pretty sure I just lack experience and or/understanding regarding concurrency, even though I've read the java tutorials etc. I think I just need a little help to get it to click.
Anyway the game runs fine until enough sounds have been played that it runs out of memory and gives me this error:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: unable to create new native thread
I was creating a new thread for each sound effect to play on because I didn't want the gui to wait for the sound to finish, and because sounds are often played very close to each other and I didn't want them conflicting on the same thread if they overlapped. The problem, I think, is that I'm not sure how to close each thread after the sound is played without stalling the main thread.
Here is the class with all the sound code. The sounds are played using the setSound() method, which sets the sound to be played and then starts a new thread with the SoundPlayer inner class for the Runnable. Thanks in advance:
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioManager {
private static Thread backgroundThread = new Thread();
private static int loopCounter = 2;
private static Clip clip;
private static String[] backgroundFiles = {
"/40_Avalon.wav","/13_Glatisant.wav",
"/31_Lying_In_Deceit.wav","/43_Return_to_Base.wav"};
private static String[] files = {
"/bigboom.wav","/Robot_blip.wav",
"/battleStations.WAV","/beep1.wav",
"/button-47.wav","/button-35.wav",
"/beep-23.wav","/Sonar_pings.wav",
"/button-21.wav","/SONAR.WAV"};
private static AudioInputStream currentBackgroundMusic;
private static AudioInputStream currentSound;
private static boolean backgroundOn = false;
private static boolean canStart = true;
private static AudioInputStream loadSound(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(files[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
private static AudioInputStream loadBackground(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(backgroundFiles[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
public static void setSound(int s){
currentSound = loadSound(s);
Thread thread = new Thread(new SoundPlayer());
thread.start();
}
private static void continueMusic(){
setBackgroundMusic(loopCounter);
loopCounter++;
if(loopCounter > 3) loopCounter = 0;
}
public static void playSound(){
try {
clip = AudioSystem.getClip();
clip.open(currentSound);
} catch (LineUnavailableException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clip.start();
}
public static void setBackgroundMusic(int s){
if (backgroundOn) {
backgroundOn = false;
canStart = false;
try {
backgroundThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
currentBackgroundMusic = loadBackground(s);
backgroundThread = new Thread(new MusicPlayer());
backgroundOn = true;
backgroundThread.start();
canStart = true;
}
private static void playSound2(AudioInputStream audio) {
AudioFormat audioFormat = audio.getFormat();
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
try{
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (Exception e)
{
e.printStackTrace();
}
line.start();
int nBytesRead = 0;
byte[] abData = new byte[128000];
while (nBytesRead != -1 && backgroundOn)
{
try{
nBytesRead = audio.read(abData, 0, abData.length);
} catch (IOException e){
e.printStackTrace();
}
if (nBytesRead == -1) break;
line.write(abData, 0, nBytesRead);
}
line.drain();
line.stop();
line.close();
line = null;
backgroundOn = false;
}
private static class MusicPlayer implements Runnable{
#Override
public void run() {
playSound2(currentBackgroundMusic);
}
}
private static class SoundPlayer implements Runnable{
#Override
public void run() {
playSound();
}
}
public static void loopMusic(){
Thread loop = new Thread(new Runnable(){
#Override
public void run() {
while(true){
if(backgroundThread.isAlive()){
try {
backgroundThread.join(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (canStart){
continueMusic();
}
}
}});
loop.start();
}
public static void reset(){
loopCounter = 2;
}
}

First of all, thank you to everyone who posted answers. You all helped a lot and the solution was a combination of your answers. I've decided to post my own answer with the solution I came up with for the benefit of others who may have the same issue.
It turns out, I was indeed creating too many threads and the OS only lets Java have a certain amount of memory space. So I fixed that by using an ExecutorService.
However, I was still having the same problem, even though I wasn't explicitly creating lots of new threads. Why? because I was creating new Clips to play sounds.
I think the Clips are somehow creating threads to play sounds on, so they can play without locking up the program or GUI (which I didn't understand before). So, to solve the problem once and for all, and also to allow my game to play the exact same sound rapidly in succession without clipping or having to wait for the previous sound to finish, I got rid of the executor and created ten Clips for each sound and that's all.
When a sound is played, it increments an index so that the next time that sound is played, it will actually use a different clip (but loaded with the same exact sound) and it prepares the next clip to play too.
My game not longer creates excessive threads or clips and runs great! The updated code is below, along with a couple of tests that I used to find out what was going on:
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioManager {
private static ExecutorService backgroundPool = Executors.newFixedThreadPool(1);
private static Future<?> backgroundStatus;
private static int loopCounter = 2;
private static String[] backgroundFiles = {
"/40_Avalon.wav","/13_Glatisant.wav",
"/31_Lying_In_Deceit.wav","/43_Return_to_Base.wav"};
private static String[] files = {
"/bigboom.wav","/Robot_blip.wav",
"/battleStations.WAV","/beep1.wav",
"/button-47.wav","/button-35.wav",
"/beep-23.wav","/Sonar_pings.wav",
"/button-21.wav","/SONAR.WAV"};
private static AudioInputStream currentBackgroundMusic;
private static boolean backgroundOn = false;
private static boolean canStart = true;
private static int[] clipIndex = new int[10];
private static Clip[][] clips = new Clip[10][10];
private static void initializeClips(int sound){
clipIndex[sound] = 0;
for (int i = 0 ; i < 10 ; i++)
try {
clips[sound][i] = AudioSystem.getClip();
clips[sound][i].open(loadSound(sound));
clips[sound][i].addLineListener(new LineListener(){
#Override
public void update(LineEvent event) {
if(event.getType() == javax.sound.sampled.LineEvent.Type.STOP){
clips[sound][clipIndex[sound]].setFramePosition(0);
}
}});
} catch (LineUnavailableException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private static AudioInputStream loadSound(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(files[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
private static AudioInputStream loadBackground(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(backgroundFiles[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
public static void setSound(int s){
if(clips[s] == null){
initializeClips(s);
}
playSound(s);
}
private static void continueMusic(){
setBackgroundMusic(loopCounter);
loopCounter++;
if(loopCounter > 3) loopCounter = 0;
}
public static void playSound(int sound){
if(clips[sound][0] == null){
initializeClips(sound);
}
clips[sound][clipIndex[sound]].start();
clipIndex[sound]++;
if(clipIndex[sound] == 10){
clipIndex[sound] = 0;
}
clips[sound][clipIndex[sound]].drain();
clips[sound][clipIndex[sound]].flush();
clips[sound][clipIndex[sound]].setFramePosition(0);
}
public static void setBackgroundMusic(int s){
canStart = false;
if (backgroundOn) {
backgroundOn = false;
}
currentBackgroundMusic = loadBackground(s);
backgroundStatus = backgroundPool.submit(new MusicPlayer());
canStart = true;
}
private static void playSound2(AudioInputStream audio) {
backgroundOn = true;
AudioFormat audioFormat = audio.getFormat();
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
try{
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (Exception e)
{
e.printStackTrace();
}
line.start();
int nBytesRead = 0;
byte[] abData = new byte[128000];
while (nBytesRead != -1 && backgroundOn){
try{
nBytesRead = audio.read(abData, 0, abData.length);
} catch (IOException e){
e.printStackTrace();
}
if (nBytesRead == -1) break;
line.write(abData, 0, nBytesRead);
}
line.drain();
line.stop();
line.close();
line = null;
backgroundOn = false;
}
private static class MusicPlayer implements Runnable{
#Override
public void run() {
playSound2(currentBackgroundMusic);
}
}
public static void loopMusic(){
Thread loop = new Thread(new Runnable(){
#Override
public void run() {
while(true){
if(backgroundStatus.isDone() && canStart){
continueMusic();
}
}
}});
loop.start();
}
public static void reset(){
loopCounter = 2;
}
}
The following is a test that will tell you how many threads your operating system lets the JVM create. Once you get the error, just look at the last number that was printed to the console.
public class Test1 {
static long count = 0L;
public static void main(String[] args) {
while(true){
count ++;
System.out.println(count);
new Thread(new Runnable(){
#Override
public void run() {
try {
Thread.sleep(60000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
}
}
}
And the following is a test that does the same thing, except by creating clips and opening resources. Notice that the clips themselves don't require a thread, but once you open them they do. You should get the same number (or close) before the error with each test. Of course, you will have to provide your own sound file to run the second one.
import java.io.IOException;
import java.util.ArrayList;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import audio.AudioManager;
public class Test2 {
static long count = 0L;
static ArrayList<Clip> clips = new ArrayList<>();
public static void main(String[] args) {
while(true){
count ++;
System.out.println(count);
try {
Clip clip1 = AudioSystem.getClip();
clip1.open(loadSound());
clips.add(clip1);
} catch (LineUnavailableException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static AudioInputStream loadSound(){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource("/bigboom.wav"));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
}

"Clip.start() method returns immediately, and the system playbacks the sound file in a background thread." (from this question which discusses how to play sounds after each other).
Since the threads you create effectively run "make clip object and start it", they actually do hardly anything. Even the I/O operations (opening the stream) are done at forehand (in the main GUI thread).
Your assumption that the GUI has to wait for a clip to finish does not appear to be valid. And I doubt they can be conflicting on the same thread if they overlap. Can you confirm the GUI is more responsive with multi-threading? My guess is that it is actually less responsive since creating and starting new threads is not cheap.

Well, as you said, your problem is that your threads are still either running or they stopped but the memory they used is not being released by java's garbage collector (GC).
A thread stops when their run() method returns (finishes) or throws an exception. If that happens, and there is NO REFERENCES to that thread anywhere in your code, it will be deleted by the GC eventually (if your program needs some memory, for example).
So, first, check that those threads you create aren't in an nasty infinite loop like the one below:
public void run() {
while(true){
//...
//Some code
//...
}
}
and once you are sure they are ending propertly after playing the sound, make sure they're not being referenced anywhere else in your code (a part of your program still maintaining a pointer to that object).
One last note. Consider checking the Thread Pool Pattern for this kind of things. I prefer it over just creating a new thread for each new task. It can be less messy and more efficient.

You need to get rid of all of these static variables. This code is not thread safe, as you are effectively trying to use all of the static variables as a shared state.
I would recommend that you start passing state around your runnable objects. If they need to communicate with each other, use the built in concurrency utilities that come with Java. If that is not sufficient, use synchronization and mutate object state.
After a cursory look at your code I can tell that your code will suffer from memory visibility issues. You could try to fix this by making your static variables volatile, but I doubt that it will be sufficient. It would make much cleaner code if you encapsulate the state within individual objects.
Before you do anything further, I would step back and try to spend 10-15 minutes and come up with an overall design. What objects will you have, what responsibilities will each one have and what state will they have (immutable/mutable). How will those objects synchronize with each other (if they run in different threads?

Your code is not thread safe.
Wait, let me get to that later. Your OOME is being caused by too many threads being created. Your current experience with concurrency is just reading online tutorials, right? Those only cover how to synchronize and share, and not how threads work.
Threads have a SIGNIFICANT setup and teardown overhead, and what you're doing is called unbounded thread creation, meaning you just create more and more threads until the system can't support it anymore.
First off, you can fix your problem using thread pooling, or more specifically, an ExecutorService to execute tasks concurrently. A cached thread pool is all you need.
Secondly, you have a ton of shared state fields. You can fix this by making an immutable wrapper for a single state snapshot every operation on an AtomicReference (or something of the like), or you can synchronize access to your fields.
Third, please get rid of all of your static fields and methods. I don't see it as appropriate in this case, although there's not enough code to validate my concern.

Related

I want to play multiple audio files at the same time using JavaSound but my threaded application skills are rusty [duplicate]

So my application should play the WAV file every time I click on the panel. But the problem right now is, it waits for the first one to finish before it plays the second one. I want to be able to have them play simultaneously.
The reason I put Thread.sleep(500) is because if I don't, then it won't play the sound at all :(
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class SoundEffectPlayer extends JFrame {
/*
* Jframe stuff
*/
public SoundEffectPlayer() {
this.setSize(400, 400);
this.setTitle("Mouse Clicker");
this.addMouseListener(new Clicker());
this.setVisible(true);
}
private class Clicker extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
try {
playSound(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
/*
* Directory of your sound files
* format is WAV
*/
private static final String DIRECTORY = "file:///C:/Users/Jessica/Desktop/audio/effects/sound 1.wav";
/*
* The volume for sound effects
*/
public static float soundEffectsVolume = 0.00f;
/*
* Loads the sound effect files from cache
* into the soundEffects array.
*/
public void playSound(int ID) throws InterruptedException {
try {
System.out.println("playing");
Clip clip;
URL url = new URL(DIRECTORY);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);
clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.setFramePosition(0);
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
gainControl.setValue(soundEffectsVolume);
clip.start();
System.out.println("played");
Thread.sleep(3000);
System.out.println("closing");
} catch (MalformedURLException e) {
System.out.println("Sound effect not found: "+ID);
e.printStackTrace();
return;
} catch (UnsupportedAudioFileException e) {
System.out.println("Unsupported format for sound: "+ID);
return;
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
}
public static void main(String[] args) throws InterruptedException {
new SoundEffectPlayer();
}
}
Update: Okay so I got them to play simeutaneously, but I want to make the the thread close when the Clip is done playing, instead of making the thread wait 500ms
How can I do that?
I have always run multiple sounds like this. I don't spawn a new thread as I guess javaSound already runs clips in an another threads. Main "game loop" may continue doing its own stuff. App may register listeners for callbacks or use getters to see what clips are doing.
Sometimes if we are to make multimedia or game application its easier to just use getters. Running gameloop 30-60fps gives enough granularity for most cases and we have a total control of what happens and when. This little testapp playbacks two wav files, first is run once, second is started after 3sec, second loops.
// java -cp ./classes SoundTest1 clip1=sound1.wav clip2=sound2.wav
import java.util.*;
import java.io.*;
import java.net.URL;
import javax.sound.sampled.*;
public class SoundTest1 {
public Clip play(String filename, boolean autostart, float gain) throws Exception {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filename));
Clip clip = AudioSystem.getClip();
clip.open(audioInputStream);
clip.setFramePosition(0);
// values have min/max values, for now don't check for outOfBounds values
FloatControl gainControl = (FloatControl)clip.getControl(FloatControl.Type.MASTER_GAIN);
gainControl.setValue(gain);
if(autostart) clip.start();
return clip;
}
public static void main(String[] args) throws Exception {
Map<String,String> params = parseParams(args);
SoundTest1 test1 = new SoundTest1();
Clip clip1 = test1.play(params.get("clip1"), true, -5.0f);
Clip clip2 = test1.play(params.get("clip2"), false, 5.0f);
final long duration=Long.MAX_VALUE;
final int interval=500;
float clip2IncGain=0.4f;
for(long ts=0; ts<duration; ts+=interval) {
System.out.println(String.format("clip1=%d/%d(%.2f), clip2=%d/%d(%.2f)"
,clip1.getFramePosition(), clip1.getFrameLength()
,((FloatControl)clip1.getControl(FloatControl.Type.MASTER_GAIN)).getValue()
,clip2.getFramePosition(), clip2.getFrameLength()
,((FloatControl)clip2.getControl(FloatControl.Type.MASTER_GAIN)).getValue()
));
if (ts>6000 && !clip2.isRunning()) {
clip2.setFramePosition(0);
clip2.start();
}
if (!clip1.isRunning()) {
clip1.close();
}
if(ts % 2000 == 0) {
// values have min/max values, for now don't check for outOfBounds values
FloatControl gainControl = (FloatControl)clip2.getControl(FloatControl.Type.MASTER_GAIN);
float oldVal=gainControl.getValue();
clip2IncGain = oldVal>=5.5 ? clip2IncGain*-1
: oldVal<=-5.5 ? clip2IncGain*-1
: clip2IncGain;
gainControl.setValue(oldVal+clip2IncGain);
}
Thread.sleep(interval);
}
}
private static Map<String,String> parseParams(String[] args) {
Map<String,String> params = new HashMap<String,String>();
for(String arg : args) {
int delim = arg.indexOf('=');
if (delim<0) params.put("", arg.trim());
else if (delim==0) params.put("", arg.substring(1).trim());
else params.put(arg.substring(0, delim).trim(), arg.substring(delim+1).trim() );
}
return params;
}
}
See JavaSound documentation for more information.
Try checking the source code of this open source soundboard program: DBoard.
You are specifically interested in using the MediaPlayer class. You can call it using
(new Thread(new MediaPlayer(PATHTOFILE)).start();

Jline input disrupted by System out

I'm having some trouble with Jline and not quite understanding how to work it properly, everything seems to work from the examples but when i attempt to move it into my console application things go weird.
I've come across two issues:
When you write input into the console while something else is logging a message via System out, the written input gets broken. (View: https://i.imgur.com/ZAJDjTI.png)
I attempted to sync the commands to the main thread since the reader thread will be blocking, but this time you'll find that this causes the output text to take over the commands input space.
((Green text is the input, white is output)View: https://i.imgur.com/CdKiIYy.png)
The output i expected was for input coming from the bottom of the console to be unaffected by the output of the console, leaving a smooth input text layer at the bottom. (View: https://i.imgur.com/HfH5l8U.png?1)
Here's an example class i wrote to demonstrate the two problems I'm having:
import jline.console.ConsoleReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
private ConsoleReader consoleReader;
private LinkedList<Runnable> syncQueue = new LinkedList<>();
private ReentrantLock lock = new ReentrantLock();
public Example() {
try {
this.consoleReader = new ConsoleReader();
this.consoleReader.setExpandEvents(false);
} catch (IOException e) {
e.printStackTrace();
}
//If you enable this, Jline seems to be disrupted by the System out.
// startStopwatch();
setupJline();
//Ticker, its ugly i know
while (true) {
lock.lock();
try {
while (syncQueue.size() > 0) {
Runnable runnable = syncQueue.poll();
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void setupJline() {
new Thread("Console Thread") {
#Override
public void run() {
while (true) {
try {
String line = consoleReader.readLine(">");
if (line != null && line.trim().length() > 0) {
//Lets pass this on as an instruction to our program
//Sync seems okay, output wise
handleInstructionSynced(line);
//async seems to mess things up though, comment the handleInstructionSynced method and
//uncomment the Async one to see what i mean.
//handleInstructionAsync(line);
}
consoleReader.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
//Just a dummy example instruction handler
private void handleInstructionAsync(String input) {
System.out.println("You've input \"" + input + "\" as your instruction.");
}
private void handleInstructionSynced(String input) {
runSync(() -> System.out.println("You've input \"" + input + "\" as your instruction."));
}
private void runSync(Runnable runnable) {
lock.lock();
try {
syncQueue.add(runnable);
} finally {
lock.unlock();
}
}
private void startStopwatch() {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
int time = 0;
#Override
public void run() {
System.out.println(time + " seconds counted");
time++;
}
};
timer.scheduleAtFixedRate(timerTask, 0, 1000);
}
public static void main(String[] args) {
new Example();
}
}
Any solutions?

Multithread programming and incrementing a static variable

I have read almost all posts related to my problem but could not solve my problem. this code is a question in the Big Java - Early Object of Cay Horstmann. The question asks about counting the word of several files by using multithread programming and store the combined words counting which is my problem.
in order to get the combined counter I used a static variable which is incremented on each of iteration of counting words in threads. Also I used a ReentrantLock() to make incrementing available only for one thread at a time.
everything works fine except incrementing. it seems sometimes the static variable does not incremented. I tested the number of lock() and unlock()
by each thread and they match with my expected result however the static variable does not work properly.
is there any explanation for it? thank you for your time and help.
my task class:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class WordCount implements Runnable
{
private String fileName;
Scanner inputFile;
private long counter;
public volatile static long combinedCounter = 0;
Lock ccLock;
public WordCount(String aName) throws FileNotFoundException
{
try
{
this.ccLock = new ReentrantLock();
this.counter = 0;
this.fileName = aName;
this.inputFile = new Scanner(new File(this.fileName));
}
catch (FileNotFoundException e)
{}
}
public void cCount()
{
ccLock.lock();
try
{
combinedCounter++;
/*synchronized (this)
{
combinedCounter++;
}*/
}
finally
{
ccLock.unlock();
}
}
#Override
public void run()
{
try
{
while (inputFile.hasNext() && !Thread.interrupted())
{
synchronized (this)
{
cCount();
}
counter++;
inputFile.next();
Thread.sleep(0);
}
System.out.printf("%s: %d\t\t%d\n", this.fileName,
this.counter,combinedCounter);
}
catch (InterruptedException e)
{}
}
}
This is my client class:
import java.io.FileNotFoundException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class WordCountRunner
{
public static void main(String[] args) throws FileNotFoundException,
InterruptedException
{
String a = "a.txt";
String b = "b.txt";
ExecutorService pool = Executors.newFixedThreadPool(2);
;
try
{
Runnable r1 = new WordCount(a);
Runnable r2 = new WordCount(b);
pool.execute(r1);
pool.execute(r2);
while (!pool.isTerminated())
{
pool.shutdown();
}
Thread.sleep(100);
System.out.print("***" + WordCount.combinedCounter);
}
catch (FileNotFoundException e)
{
}
finally
{
pool.shutdown();
}
}
}
The lock does not work because the ReentrantLock is an instance variable in the WordCount class. So, each instance of that class has its own private lock and they don't synchronize with each other. The easiest change would be to make the lock static, like the variable it's protecting.
Each of your Runnables has its own lock object. For your strategy to work, they all need to share exactly one lock.
For example,
// ...
static Lock ccLock = new ReentrantLock();
public WordCount(String aName) throws FileNotFoundException
{
try
{
// this.ccLock = new ReentrantLock();
this.counter = 0;
this.fileName = aName;
this.inputFile = new Scanner(new File(this.fileName));
// ...
}

(Java) exiting a loop "remotely"

I have a piece of Java program that essentially does the following:
public static void main(String[] args)
{
while(true)
{
// does stuff ...
}
}
The infinite loop is there by design - when left alone the program will loop infinitely. For the most part it works fine. However, sometimes I want to take the program down for maintenance, and when I take it down I want to make sure that it runs through all the code in the loop to the end then exit.
I am wondering what is the best solution for this. One idea I have in mind is to do something like this:
public static void main(String[] args)
{
File f = new File("C:\exit.txt");
while(!f.exists())
{
// does stuff ...
}
}
which basically allows me to gracefully get out of the loop by creating a file called "exit.txt". This is probably OK for my purposes, but I would like to know if there are better, alternative methods.
I think that the WatchService that was introduced in Java 7 may be of use here (if you prefer a file based approach that is). From the JavaDocs:
A watch service that watches registered objects for changes and events. For example a file manager may use a watch service to monitor a directory for changes so that it can update its display of the list of files when files are created or deleted.
Basically what this means is that you can set up a WatchService that can watch a folder for changes. When a change occurs you can choose what actions to take.
The following code uses the WatchService to monitor a specified folder for changes. When a change has happened it executes a Runnable that the caller has provided (the method runWhenItIsTimeToExit).
public class ExitChecker {
private final Path dir;
private final Executor executor;
private final WatchService watcher;
// Create the checker using the provided path but with some defaults for
// executor and watch service
public ExitChecker(final Path dir) throws IOException {
this(dir, FileSystems.getDefault().newWatchService(), Executors.newFixedThreadPool(1));
}
// Create the checker using the provided path, watcher and executor
public ExitChecker(final Path dir, final WatchService watcher, final Executor executor) {
this.dir = dir;
this.watcher = watcher;
this.executor = executor;
}
// Wait for the folder to be modified, then invoke the provided runnable
public void runWhenItIsTimeToExit(final Runnable action) throws IOException {
// Listen on events in the provided folder
dir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
// Run it async, otherwise the caller thread will be blocked
CompletableFuture.runAsync(() -> {
try {
watcher.take();
} catch (InterruptedException e) {
// Ok, we got interrupted
}
}, executor).thenRunAsync(action);
}
}
So, how do we use the checker then? Well, the following code illustrates this:
public static void main(String... args) throws IOException, InterruptedException {
// Setup dirs in the home folder
final Path directory = Files.createDirectories(
new File(System.getProperty("user.home") + "/.exittst").toPath());
// In this case we use an AtomicBoolean to hold the "exit-status"
AtomicBoolean shouldExit = new AtomicBoolean(false);
// Start the exit checker, provide a Runnable that will be executed
// when it is time to exit the program
new ExitChecker(directory).runWhenItIsTimeToExit(() -> {
// This is where your exit code will end up. In this case we
// simply change the value of the AtomicBoolean
shouldExit.set(true);
});
// Start processing
while (!shouldExit.get()) {
System.out.println("Do something in loop");
Thread.sleep(1000);
}
System.out.println("Exiting");
}
Finally, how do you exit the program then? Well simply touch a file in the specified folder. Example:
cd ~/.exittst
touch exit-now.please
Resources:
A good tutorial on how to use the WatchService
WatchService JavaDocs
A good article about CompletableFuture
More stuff about CompletableFuture
Why the WatchService is slow on Mac OS X
One could employ some sophisticated techniques here. The file watchdog is one option. RMI could be another. But in fact, the mechanisms that are required here are quite simple, so I'd like to propose another (very simple) solution.
Note: This solution is just one option, showing that it is possible to do it that way. It is not a general recommendation, and whether it is "good" or not depends on the application case.
The solution is simply based on Sockets. The ServerSocket#accept method already encapsulates the functionality that you want:
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
Based on this, it is trivial to create such a "remote control": The server just waits for a connection, and sets a flag when the connection is opened:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean;
class RemoteExitServer
{
private final AtomicBoolean flag = new AtomicBoolean();
RemoteExitServer()
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
waitForConnection();
}
});
t.setDaemon(true);
t.start();
}
private void waitForConnection()
{
ServerSocket server = null;
Socket socket = null;
try
{
server = new ServerSocket(1234);
socket = server.accept();
flag.set(true);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (server != null)
{
try
{
server.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
boolean shouldExit()
{
return flag.get();
}
}
The client does exactly that: It opens a connection, and nothing else
import java.io.IOException;
import java.net.Socket;
public class RemoteExitClient
{
public static void main(String[] args)
{
Socket socket = null;
try
{
socket = new Socket("localhost", 1234);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
The application is then also very simple:
public class RemoteExitTest
{
public static void main(String[] args)
{
RemoteExitServer e = new RemoteExitServer();
while (!e.shouldExit())
{
System.out.println("Working...");
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
}
System.out.println("done");
}
}
(The code could be made even more concise with try-with-resources, but this should not matter here)
You could make use of runtime shutdown hook. That way you won't need to use console input in order to stop the loop. If JVM is being closed normally then shutdown hook thread will run. This thread will wait for the end of current loop iteration. Keep in mind that there are some limitations when using hooks though: https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-
import java.util.concurrent.CountDownLatch;
public class Test {
private volatile static CountDownLatch lastIterationLatch = null;
private static boolean stop = false;
public static void main(String [] args) throws Exception {
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
lastIterationLatch = new CountDownLatch(1);
try {
lastIterationLatch.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
while(!stop) {
System.out.println("iteration start");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("iteration end");
if(lastIterationLatch != null) {
stop = true;
lastIterationLatch.countDown();
}
}
}
}
For something quick/dirty, use Signals:
boolean done = false;
// ...
Signal.handle(new Signal("USR1"), new SignalHandler() {
#Override
public void handle(Signal signal) {
// signal triggered ...
done = true;
}
});
// ...
while(!done) { ... }
Then, use kill -USR1 _pid_ to trigger the signal.
You could use a AtomicBoolean as in the test program below.
To suspend just type true into the console to resume type false. The program will never exit.
public class Test2 {
public static void main(String[] args) {
final AtomicBoolean suspended = new AtomicBoolean(false);
new Thread() {
public void run() {
while (true)
{
Scanner sc = new Scanner(System.in);
boolean b = sc.nextBoolean();
suspended.set(b);
}
}
}.start();
while(true){
if(!suspended.get()){
System.out.println("working");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
//System.exit(0) //if you want to exit rather than suspend uncomment.
}
}
}
}

Problem with Javas Audio Clips on frequent playback of beep sounds

I want to playback short beep sounds (WAV files) on success and error of a GUI triggered action.
I came across javax.sound.sampled.Clip, which seemed to work.
Here is the basic code I use:
clip.stop();
clip.setFramePosition(0);
clip.start();
This is executed after a button click triggers a database action. On success and error two different preloaded Clips are played.
But on the production machine (an old PC running Kubuntu 10.4) after some time (around 400+ executions or 2-4 hours) the clip refuses to play.
The stop method takes around 3 seconds to terminate and the following start action does not play any sound. Every following invocation of the code fails then without throwing exception or any other feedback.
The only thing, that fixes this, is restarting the whole application.
My questions are:
Is there any workaround for this? Does anyone else have the same problem? Or is there another framework I can use to play at least two different sounds (the Toolkit.beep() can only play one sound).
Don't be afraid to just recreate objects, the overhead is low. Instead of resetting the clips, try just creating new ones. You could cache the files, that would be a useful optimization. Reusing the clip objects is not.
Or you could try an alternative implementation [that is not restricted].
This is the top result in Google for 'java play wav files':
http://www.anyexample.com/programming/java/java_play_wav_sound_file.xml
It simplifies things down to a single call:
new AePlayWave("test.wav").start();
Just add this class to your codebase:
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AePlayWave extends Thread {
private String filename;
private Position curPosition;
private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
enum Position {
LEFT, RIGHT, NORMAL
};
public AePlayWave(String wavfile) {
filename = wavfile;
curPosition = Position.NORMAL;
}
public AePlayWave(String wavfile, Position p) {
filename = wavfile;
curPosition = p;
}
public void run() {
File soundFile = new File(filename);
if (!soundFile.exists()) {
System.err.println("Wave file not found: " + filename);
return;
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
return;
} catch (IOException e1) {
e1.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
if (auline.isControlSupported(FloatControl.Type.PAN)) {
FloatControl pan = (FloatControl) auline
.getControl(FloatControl.Type.PAN);
if (curPosition == Position.RIGHT)
pan.setValue(1.0f);
else if (curPosition == Position.LEFT)
pan.setValue(-1.0f);
}
auline.start();
int nBytesRead = 0;
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0)
auline.write(abData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
auline.drain();
auline.close();
}
}
}
So how i fixed it:
I basically followed the hint from Charles Goodwin, but i changed the AePlayWave class to implement runnable and use it with a thread pool (to avoid overhead from starting new Threads all the time). Also, i use URLs and not Files to use resources from within the packed JAR file. The AePlayWave Object is created when setup is done (the files are chosen) or settings change. There is an instance for every sound I want the application to play. The listener methods for the events then trigger the pool to run the specific AePlayWave instance for that events sound. The rest is basically the same.
There are only two inconvenient issues:
1.) on weak machines, the ending of a WAV is not always played. When sounds are very short (like 100ms beeps), this might lead to no sound being played at all! Thats why i added 500 ms of silence to the end of each sound i'd like to play. It's a workaround, but it helps and for now it seems to be the best and most stable approach.
2.) If more than one sound is played (because of very quick repetition) the sounds overlap and you hear a change in tune and volume. This is ok in my case but might be annoying for other uses.
It is already running on the productive system. If any errors are reported to me, i will edit this post to keep you up to date.
Now here is the (basically reduced) sourcecode:
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudibleListener implements SomeListener {
private Runnable successRunner;
private Runnable failRunner;
ExecutorService pool = Executors.newCachedThreadPool();
/**
* Call this after initialization and after every change in your config at runtime.
*/
public void reloadSettings() {
// put your configuration here
this.successRunner = new WavePlayer(this.getClass().getResource("success.wav"));
this.failRunner = new WavePlayer(this.getClass().getResource("fail.wav"));
}
/**
* Call this to savely shutdown the thread pool.
*/
public void shutdown() {
this.pool.shutdown();
}
/**
* Listener method called on success.
*/
public void eventSuccess() {
this.pool.execute(this.successRunner);
}
/**
* Listener method called on fail.
*/
public void eventFailed() {
this.pool.execute(this.failRunner);
}
private class WavePlayer implements Runnable {
private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
private URL soundFile;
public WavePlayer(URL soundFile) {
this.soundFile = soundFile;
}
#Override
public void run() {
try {
// check if the URL is still accessible!
this.soundFile.openConnection().connect();
this.soundFile.openStream().close();
} catch (Exception e) {
return;
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem
.getAudioInputStream(this.soundFile);
} catch (UnsupportedAudioFileException e) {
return;
} catch (IOException e) {
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine auline = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
return;
} catch (Exception e) {
return;
}
auline.start();
int nBytesRead = 0;
byte[] abData = new byte[this.EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream
.read(abData, 0, abData.length);
if (nBytesRead >= 0) {
auline.write(abData, 0, nBytesRead);
}
}
} catch (IOException e) {
return;
} finally {
auline.drain();
auline.close();
}
}
}
}
Cheers and thanks so far for all the help!
P.
Update:
This is now running for the last 72 hours without any errors! Looks like we made it!

Categories

Resources