Other |
---|
5.0 tigerFixed |
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Relates :
|
|
Relates :
|
Name: rmT116609 Date: 08/22/2002 FULL PRODUCT VERSION : java version "1.4.1-rc" Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-rc-b19) Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode) FULL OPERATING SYSTEM VERSION : Microsoft Windows 2000 [Version 5.00.2195] A DESCRIPTION OF THE PROBLEM : When a clip line is obtained, played, and closed, two threads remain. 1 "Java Sound event dispatcher" 2 Daemon "Headspace mixer frame proc thread" In a console application, the first thread, since it is not a Daemon, will keep the application from exiting until that thread is interrupted. This is not a problem in a Swing application. The problem is two-fold: 1 Console application cannot exit properly unless they find the thread by name and interrupt it. This is not a solution that is sure to be portable across future Java Sound API versions. 2 When any type of Line is obtained, these thread resources are allocated. They stay allocated for the lifetime of the application. I only know of the threads at this time but there may be other Java Sound resources that are hanging around as well. This is poor resource management. It is fine to allocate them at first need, but there must also be an API to: * Detect if Java Sound resources are allocated. * Release Java Sound resources on demand as long as they are not needed. * Pre-allocate Java Sound resources in anticipation of their need. The following simple console application demonstrates this behavior. If not run with the "-i" option, the given sound file will play but the application will not exit. The thread dump will show the Java Sound threads, of which the non-Daemon thread is the culprit. If it is run with the "-i" option, the application will exit and the thread dump will show the non-Daemon Java Sound thread no longer exists. STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : 1. In a console application, get a "javax.sound.sampled.Line", which will automatically allocate Java Sound resources among which is the non-Daemon "Java Sound event dispatcher" thread. 2. Try to exit the cosole application. EXPECTED VERSUS ACTUAL BEHAVIOR : Expected: The console application exits. Actual : The console application hangs until ctrl-c is pressed. REPRODUCIBILITY : This bug can be reproduced always. ---------- BEGIN SOURCE ---------- import java.io.*; import javax.sound.sampled.*; /** * A simple sound file playing console application using the Java Sound API. * * @author Curtis Clauson for The Snake Pit - Development */ public class PlaySound { /* * Class Methods */ /* * Plays the given sound file. * * @args args The array of command line argument Strings. */ public static void main(String[] args) { // Parse arguments. final int argCount = args.length; if (argCount == 0 || argCount > 2) { System.err.println( "ERROR: Invalid number of arguments.\n" + "usage: java PlaySound [options] <wave file path>\n" + "Options:\n" + " -i Interrupt the \"Java Sound event dispatcher\" thread on exit." ); return; } boolean interruptThread = false; File file = null; for (int iArg = 0; iArg < argCount; iArg++) { String arg = args[iArg]; if (arg.equals("-i")) interruptThread = true; else { file = new File(arg); if (!file.exists() || file.isDirectory()) { System.err.println("ERROR: File not found."); return; } } } if (file == null) { System.err.println("ERROR: No sound file was given."); return; } // Play the sound file. PlaySound playWav = new PlaySound(file); playWav.play(); // Deal with and show the active threads. System.out.println("Done."); if (interruptThread) { Thread thread = Utilities.getThread("Java Sound event dispatcher"); if (thread != null) thread.interrupt(); } Utilities.listThreads(); } /* * Instance Data */ /** The sound clip of the given file. */ Clip clip; /* * Constructors */ /* * Create a PlaySound class. */ public PlaySound(final File file) { // Get an AudioInputStream. AudioInputStream is; try { is = AudioSystem.getAudioInputStream(file); } catch (UnsupportedAudioFileException e) { System.err.println("Unknown sampled file format."); return; } catch (IOException e) { System.err.println("Error reading the file."); return; } System.out.println("File: " + file.getName() + " Frame Length: " + is.getFrameLength() + " Format: " + is.getFormat().getEncoding().toString()); // Get a clip. try { clip = (Clip)AudioSystem.getLine(new Line.Info(Clip.class)); } catch (LineUnavailableException e) { System.err.println("No Clip was available."); return; } System.out.println("Clip: " + clip.getLineInfo().toString()); clip.addLineListener(new LineListener() { public void update(LineEvent event) { LineEvent.Type type = event.getType(); System.out.println("*** Line Event: " + type.toString() + " ***"); if (type == type.STOP) { clip.close(); } else if (type == type.CLOSE) { synchronized(PlaySound.this) { PlaySound.this.notify(); } } } }); // Open and load the clip from the AudioInputStream. try { clip.open(is); } catch (LineUnavailableException e) { System.err.println("No Clip was available."); return; } catch (IOException e) { System.err.println("Error reading the file."); return; } } /* * Instance Methods */ /** * Play the clip and wait until it finishes. */ public synchronized void play() { if (clip == null) return; clip.start(); try {wait();} catch (InterruptedException e) {} } } /** * Thread utility methods. */ class Utilities { /* * Class Methods */ /** * List all the active thread groups and threads on the console. */ public static Thread getThread(String name) { ThreadGroup root; for (root = Thread.currentThread().getThreadGroup(); root.getParent() != null; root = root.getParent()); Thread[] threads = new Thread[root.activeCount()]; int count = root.enumerate(threads); for (int iThread = 0; iThread < count; iThread++) { Thread thread = threads[iThread]; if (thread.getName().equals(name)) return thread; } return null; } /** * List all the active thread groups and threads on the console. */ public static void listThreads() { ThreadGroup tgRoot; for (tgRoot = Thread.currentThread().getThreadGroup(); tgRoot.getParent() != null; tgRoot = tgRoot.getParent()); listThreads(tgRoot, 0); } /** * Recursive method that lists all the thread groups and threads in the given ThreadGroup. */ protected static void listThreads(final ThreadGroup root, final int nestLevel) { for (int index = 0; index < nestLevel; index++) System.out.print(" "); System.out.println("ThreadGroup: " + root.getName()); ThreadGroup[] groups = new ThreadGroup[root.activeGroupCount()]; int count = root.enumerate(groups, false); for (int iGroup = 0; iGroup < count; iGroup++) { ThreadGroup group = groups[iGroup]; listThreads(group, nestLevel + 1); } Thread[] threads = new Thread[root.activeCount()]; count = root.enumerate(threads, false); for (int iThread = 0; iThread < count; iThread++) { Thread thread = threads[iThread]; for (int index = 0; index < nestLevel; index++) System.out.print(" "); System.out.println( " Thread: " + (thread.isDaemon() ? 'D' : ' ') + (thread.isAlive() ? 'A' : ' ') + (thread.isInterrupted() ? 'I' : ' ') + " " + thread.getName() ); } } } ---------- END SOURCE ---------- CUSTOMER WORKAROUND : In your console application, find the active non-Daemon "Java Sound event dispatcher" Thread object and call the interrupt() method. The console application will then be able to exit. Note that, since you must depend on the name of the thread, this work around is not sure to be portable across future versions. (Review ID: 160615) ======================================================================
|