今天看了源码,总算多少理解一点,对Runnable 和 Thread的区别
一句话,Runnable是接口,Thread 继承了Runnable, 它们都有run()方法。
public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }
public class Thread implements Runnable { /* Make sure registerNatives is the first thing <clinit> does. */ private static native void registerNatives(); static { registerNatives(); } private char name[]; private int priority; private Thread threadQ; private long eetop; /* Whether or not to single_step this thread. */ private boolean single_step; /* Whether or not the thread is a daemon thread. */ private boolean daemon = false; /* JVM state */ private boolean stillborn = false; /* What will be run. */ private Runnable target; /* The group of this thread */ private ThreadGroup group; /* The context ClassLoader for this thread */ private ClassLoader contextClassLoader; /* The inherited AccessControlContext of this thread */ private AccessControlContext inheritedAccessControlContext; /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } ThreadLocal.ThreadLocalMap threadLocals = null; ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; /* * The requested stack size for this thread, or 0 if the creator did not * specify a stack size. It is up to the VM to do whatever it likes with * this number; some VMs will ignore it. */ private long stackSize; /* * JVM-private state that persists after native thread termination. */ private long nativeParkEventPointer; private long tid; private int threadStatus = 0; /* For generating thread ID */ private static long threadSeqNumber; private static synchronized long nextThreadID() { return ++threadSeqNumber; } /** * The argument supplied to the current call to * java.util.concurrent.locks.LockSupport.park. Set by (private) * java.util.concurrent.locks.LockSupport.setBlocker Accessed using * java.util.concurrent.locks.LockSupport.getBlocker */ volatile Object parkBlocker; /* * The object in which this thread is blocked in an interruptible I/O * operation, if any. The blocker's interrupt method should be invoked after * setting this thread's interrupt status. */ private volatile Interruptible blocker; private Object blockerLock = new Object(); /* * Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio * code */ void blockedOn(Interruptible b) { synchronized (blockerLock) { blocker = b; } } public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10; /* If stop was called before start */ private boolean stopBeforeStart; /* Remembered Throwable from stop before start */ private Throwable throwableFromStop; /** * Returns a reference to the currently executing thread object. * * @return the currently executing thread. */ public static native Thread currentThread(); /** * Causes the currently executing thread object to temporarily pause and * allow other threads to execute. */ public static native void yield(); /** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to the * precision and accuracy of system timers and schedulers. The thread does * not lose ownership of any monitors. * * @param millis * the length of time to sleep in milliseconds. * @exception InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is cleared * when this exception is thrown. * @see Object#notify() */ public static native void sleep(long millis) throws InterruptedException; /** * Causes the currently executing thread to sleep (cease execution) for the * specified number of milliseconds plus the specified number of * nanoseconds, subject to the precision and accuracy of system timers and * schedulers. The thread does not lose ownership of any monitors. * * @param millis * the length of time to sleep in milliseconds. * @param nanos * 0-999999 additional nanoseconds to sleep. * ... */ public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } /** * Initializes a Thread. * ... */ private void init(ThreadGroup g, Runnable target, String name, long stackSize) { Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security != null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); } } /* * checkAccess regardless of whether or not threadgroup is explicitly * passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); this.name = name.toCharArray(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = AccessController.getContext(); this.target = target; setPriority(priority); if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal .createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); } public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } public Thread(String name) { init(null, null, name, 0); } public Thread(ThreadGroup group, String name) { init(group, null, name, 0); } public Thread(Runnable target, String name) { init(null, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); } /** * Causes this thread to begin execution; the Java Virtual Machine calls the * <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the current * thread (which returns from the call to the <code>start</code> method) and * the other thread (which executes its <code>run</code> method). * <p> * It is never legal to start a thread more than once. In particular, a * thread may not be restarted once it has completed execution. * * @exception IllegalThreadStateException * if the thread was already started. * @see #run() * @see #stop() */ public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); start0(); if (stopBeforeStart) { stop0(throwableFromStop); } } private native void start0(); /** * If this thread was constructed using a separate <code>Runnable</code> run * object, then that <code>Runnable</code> object's <code>run</code> method * is called; otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ public void run() { if (target != null) { target.run(); } } /** * This method is called by the system to give a Thread a chance to clean up * before it actually exits. */ private void exit() { if (group != null) { group.remove(this); group = null; } /* Aggressively null out all reference fields: see bug 4006245 */ target = null; /* Speed the release of some of these resources */ threadLocals = null; inheritableThreadLocals = null; inheritedAccessControlContext = null; blocker = null; uncaughtExceptionHandler = null; } @Deprecated public final void stop() { // If the thread is already dead, return. // A zero status value corresponds to "NEW". if ((threadStatus != 0) && !isAlive()) { return; } stop1(new ThreadDeath()); } @Deprecated public final synchronized void stop(Throwable obj) { stop1(obj); } /** * Common impl for stop() and stop(Throwable). */ private final synchronized void stop1(Throwable th) { SecurityManager security = System.getSecurityManager(); if (security != null) { checkAccess(); if ((this != Thread.currentThread()) || (!(th instanceof ThreadDeath))) { security .checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); } } // A zero status value corresponds to "NEW" if (threadStatus != 0) { resume(); // Wake up thread if it was suspended; no-op otherwise stop0(th); } else { // Must do the null arg check that the VM would do with stop0 if (th == null) { throw new NullPointerException(); } // Remember this stop attempt for if/when start is used stopBeforeStart = true; throwableFromStop = th; } } /** * Interrupts this thread. * * <p> * Unless the current thread is interrupting itself, which is always * permitted, the {@link #checkAccess() checkAccess} method of this thread * is invoked, which may cause a {@link SecurityException} to be thrown. * * <p> * If this thread is blocked in an invocation of the {@link Object#wait() * wait()}, {@link Object#wait(long) wait(long)}, or * {@link Object#wait(long, int) wait(long, int)} methods of the * {@link Object} class, or of the {@link #join()}, {@link #join(long)}, * {@link #join(long, int)}, {@link #sleep(long)}, or * {@link #sleep(long, int)}, methods of this class, then its interrupt * status will be cleared and it will receive an * {@link InterruptedException}. * * <p> * If this thread is blocked in an I/O operation upon an * {@link java.nio.channels.InterruptibleChannel </code>interruptible * channel<code>} then the channel will be closed, the thread's interrupt * status will be set, and the thread will receive a * {@link java.nio.channels.ClosedByInterruptException}. * * <p> If this thread is blocked in a {@link java.nio.channels.Selector} * then the thread's interrupt status will be set and it will return * immediately from the selection operation, possibly with a non-zero value, * just as if the selector's {@link java.nio.channels.Selector#wakeup * wakeup} method were invoked. * * <p> If none of the previous conditions hold then this thread's interrupt * status will be set. * </p> * * <p> * Interrupting a thread that is not alive need not have any effect. * * @throws SecurityException * if the current thread cannot modify this thread * * @revised 6.0 * @spec JSR-51 */ public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(); return; } } interrupt0(); } /** * Tests whether the current thread has been interrupted. The <i>interrupted * status</i> of the thread is cleared by this method. In other words, if * this method were to be called twice in succession, the second call would * return false (unless the current thread were interrupted again, after the * first call had cleared its interrupted status and before the second call * had examined it). * * <p> * A thread interruption ignored because a thread was not alive at the time * of the interrupt will be reflected by this method returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */ public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p> * A thread interruption ignored because a thread was not alive at the time * of the interrupt will be reflected by this method returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted() { return isInterrupted(false); } /** * Tests if some Thread has been interrupted. The interrupted state is reset * or not based on the value of ClearInterrupted that is passed. */ private native boolean isInterrupted(boolean ClearInterrupted); @Deprecated public void destroy() { throw new NoSuchMethodError(); } /** * Tests if this thread is alive. A thread is alive if it has been started * and has not yet died. * * @return <code>true</code> if this thread is alive; <code>false</code> * otherwise. */ public final native boolean isAlive(); @Deprecated public final void suspend() { checkAccess(); suspend0(); } @Deprecated public final void resume() { checkAccess(); resume0(); } /** * Changes the priority of this thread. * <p> * First the <code>checkAccess</code> method of this thread is called with * no arguments. This may result in throwing a * <code>SecurityException</code>. * <p> * Otherwise, the priority of this thread is set to the smaller of the * specified <code>newPriority</code> and the maximum permitted priority of * the thread's thread group. * * @param newPriority * priority to set this thread to * @exception IllegalArgumentException * If the priority is not in the range * <code>MIN_PRIORITY</code> to <code>MAX_PRIORITY</code>. * @exception SecurityException * if the current thread cannot modify this thread. * @see #getPriority * @see #checkAccess() * @see #getThreadGroup() * @see #MAX_PRIORITY * @see #MIN_PRIORITY * @see ThreadGroup#getMaxPriority() */ public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if ((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } /** * Returns this thread's priority. * * @return this thread's priority. * @see #setPriority */ public final int getPriority() { return priority; } /** * Changes the name of this thread to be equal to the argument * <code>name</code>. * <p> * First the <code>checkAccess</code> method of this thread is called with * no arguments. This may result in throwing a * <code>SecurityException</code>. * * @param name * the new name for this thread. * @exception SecurityException * if the current thread cannot modify this thread. * @see #getName * @see #checkAccess() */ public final void setName(String name) { checkAccess(); this.name = name.toCharArray(); } /** * Returns this thread's name. * * @return this thread's name. * @see #setName(String) */ public final String getName() { return String.valueOf(name); } /** * Returns the thread group to which this thread belongs. This method * returns null if this thread has died (been stopped). * * @return this thread's thread group. */ public final ThreadGroup getThreadGroup() { return group; } /** * Returns the number of active threads in the current thread's thread * group. * * @return the number of active threads in the current thread's thread * group. */ public static int activeCount() { return currentThread().getThreadGroup().activeCount(); } /** * Copies into the specified array every active thread in the current * thread's thread group and its subgroups. This method simply calls the * <code>enumerate</code> method of the current thread's thread group with * the array argument. * <p> * First, if there is a security manager, that <code>enumerate</code> method * calls the security manager's <code>checkAccess</code> method with the * thread group as its argument. This may result in throwing a * <code>SecurityException</code>. * * @param tarray * an array of Thread objects to copy to * @return the number of threads put into the array * @exception SecurityException * if a security manager exists and its * <code>checkAccess</code> method doesn't allow the * operation. * @see ThreadGroup#enumerate(Thread[]) * @see SecurityManager#checkAccess(ThreadGroup) */ public static int enumerate(Thread tarray[]) { return currentThread().getThreadGroup().enumerate(tarray); } @Deprecated public native int countStackFrames(); /** * Waits at most <code>millis</code> milliseconds for this thread to die. A * timeout of <code>0</code> means to wait forever. * * @param millis * the time to wait in milliseconds. * @exception InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is cleared * when this exception is thrown. */ public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } /** * Waits at most <code>millis</code> milliseconds plus <code>nanos</code> * nanoseconds for this thread to die. * * @param millis * the time to wait in milliseconds. * @param nanos * 0-999999 additional nanoseconds to wait. * @exception IllegalArgumentException * if the value of millis is negative the value of nanos is * not in the range 0-999999. * @exception InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is cleared * when this exception is thrown. */ public final synchronized void join(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } join(millis); } /** * Waits for this thread to die. * * @exception InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is cleared * when this exception is thrown. */ public final void join() throws InterruptedException { join(0); } /** * Prints a stack trace of the current thread to the standard error stream. * This method is used only for debugging. * * @see Throwable#printStackTrace() */ public static void dumpStack() { new Exception("Stack trace").printStackTrace(); } /** * Marks this thread as either a daemon thread or a user thread. The Java * Virtual Machine exits when the only threads running are all daemon * threads. * <p> * This method must be called before the thread is started. * <p> * This method first calls the <code>checkAccess</code> method of this * thread with no arguments. This may result in throwing a * <code>SecurityException </code>(in the current thread). * * @param on * if <code>true</code>, marks this thread as a daemon thread. * @exception IllegalThreadStateException * if this thread is active. * @exception SecurityException * if the current thread cannot modify this thread. * @see #isDaemon() * @see #checkAccess */ public final void setDaemon(boolean on) { checkAccess(); if (isAlive()) { throw new IllegalThreadStateException(); } daemon = on; } /** * Tests if this thread is a daemon thread. * * @return <code>true</code> if this thread is a daemon thread; * <code>false</code> otherwise. * @see #setDaemon(boolean) */ public final boolean isDaemon() { return daemon; } /** * Determines if the currently running thread has permission to modify this * thread. * <p> * If there is a security manager, its <code>checkAccess</code> method is * called with this thread as its argument. This may result in throwing a * <code>SecurityException</code>. * * @exception SecurityException * if the current thread is not allowed to access this * thread. * @see SecurityManager#checkAccess(Thread) */ public final void checkAccess() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkAccess(this); } } public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } } /** * Returns the context ClassLoader for this Thread. The context ClassLoader * is provided by the creator of the thread for use by code running in this * thread when loading classes and resources. If not set, the default is the * ClassLoader context of the parent Thread. The context ClassLoader of the * primordial thread is typically set to the class loader used to load the * application. * * <p> * First, if there is a security manager, and the caller's class loader is * not null and the caller's class loader is not the same as or an ancestor * of the context class loader for the thread whose context class loader is * being requested, then the security manager's <code>checkPermission</code> * method is called with a <code>RuntimePermission("getClassLoader")</code> * permission to see if it's ok to get the context ClassLoader.. * * @return the context ClassLoader for this Thread * * @throws SecurityException * if a security manager exists and its * <code>checkPermission</code> method doesn't allow getting the * context ClassLoader. * @see #setContextClassLoader * @see SecurityManager#checkPermission * @see RuntimePermission * * @since 1.2 */ public ClassLoader getContextClassLoader() { if (contextClassLoader == null) return null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { ClassLoader ccl = ClassLoader.getCallerClassLoader(); if (ccl != null && ccl != contextClassLoader && !contextClassLoader.isAncestor(ccl)) { sm .checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION); } } return contextClassLoader; } public void setContextClassLoader(ClassLoader cl) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("setContextClassLoader")); } contextClassLoader = cl; } /** * Returns <tt>true</tt> if and only if the current thread holds the monitor * lock on the specified object. * * <p> * This method is designed to allow a program to assert that the current * thread already holds a specified lock: * * <pre> * assert Thread.holdsLock(obj); * </pre> * * @param obj * the object on which to test lock ownership * @throws NullPointerException * if obj is <tt>null</tt> * @return <tt>true</tt> if the current thread holds the monitor lock on the * specified object. * @since 1.4 */ public static native boolean holdsLock(Object obj); private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; public StackTraceElement[] getStackTrace() { if (this != Thread.currentThread()) { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { security .checkPermission(SecurityConstants.GET_STACK_TRACE_PERMISSION); } if (!isAlive()) { return EMPTY_STACK_TRACE; } return dumpThreads(new Thread[] { this })[0]; } else { // Don't need JVM help for current thread return (new Exception()).getStackTrace(); } } public static Map<Thread, StackTraceElement[]> getAllStackTraces() { // check for getStackTrace permission SecurityManager security = System.getSecurityManager(); if (security != null) { security .checkPermission(SecurityConstants.GET_STACK_TRACE_PERMISSION); security .checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION); } // Get a snapshot of the list of all threads Thread[] threads = getThreads(); StackTraceElement[][] traces = dumpThreads(threads); Map<Thread, StackTraceElement[]> m = new HashMap<Thread, StackTraceElement[]>( threads.length); for (int i = 0; i < threads.length; i++) { if (threads[i].isAlive()) { StackTraceElement[] stackTrace = traces[i]; if (stackTrace == null) { stackTrace = EMPTY_STACK_TRACE; } m.put(threads[i], stackTrace); } } return m; } private static final RuntimePermission SUBCLASS_IMPLEMENTATION_PERMISSION = new RuntimePermission( "enableContextClassLoaderOverride"); /** cache of subclass security audit results */ private static final SoftCache subclassAudits = new SoftCache(10); /** * Verifies that this (possibly subclass) instance can be constructed * without violating security constraints: the subclass must not override * security-sensitive non-final methods, or else the * "enableContextClassLoaderOverride" RuntimePermission is checked. */ private static boolean isCCLOverridden(Class cl) { if (cl == Thread.class) return false; Boolean result = null; synchronized (subclassAudits) { result = (Boolean) subclassAudits.get(cl); if (result == null) { /* * Note: only new Boolean instances (i.e., not Boolean.TRUE or * Boolean.FALSE) must be used as cache values, otherwise cache * entry will pin associated class. */ result = new Boolean(auditSubclass(cl)); subclassAudits.put(cl, result); } } return result.booleanValue(); } /** * Performs reflective checks on given subclass to verify that it doesn't * override security-sensitive non-final methods. Returns true if the * subclass overrides any of the methods, false otherwise. */ private static boolean auditSubclass(final Class subcl) { Boolean result = (Boolean) AccessController .doPrivileged(new PrivilegedAction() { public Object run() { for (Class cl = subcl; cl != Thread.class; cl = cl .getSuperclass()) { try { cl.getDeclaredMethod("getContextClassLoader", new Class[0]); return Boolean.TRUE; } catch (NoSuchMethodException ex) { } try { Class[] params = { ClassLoader.class }; cl.getDeclaredMethod("setContextClassLoader", params); return Boolean.TRUE; } catch (NoSuchMethodException ex) { } } return Boolean.FALSE; } }); return result.booleanValue(); } private native static StackTraceElement[][] dumpThreads(Thread[] threads); private native static Thread[] getThreads(); public long getId() { return tid; } public enum State { NEW, RUNNABLE, WAITING, TIMED_WAITING, TERMINATED; } public State getState() { return sun.misc.VM.toThreadState(threadStatus); } // Added in JSR-166 /** * Interface for handlers invoked when a <tt>Thread</tt> abruptly terminates * due to an uncaught exception. * <p> * When a thread is about to terminate due to an uncaught exception the Java * Virtual Machine will query the thread for its * <tt>UncaughtExceptionHandler</tt> using * {@link #getUncaughtExceptionHandler} and will invoke the handler's * <tt>uncaughtException</tt> method, passing the thread and the exception * as arguments. If a thread has not had its * <tt>UncaughtExceptionHandler</tt> explicitly set, then its * <tt>ThreadGroup</tt> object acts as its <tt>UncaughtExceptionHandler</tt> * . If the <tt>ThreadGroup</tt> object has no special requirements for * dealing with the exception, it can forward the invocation to the * {@linkplain #getDefaultUncaughtExceptionHandler default uncaught * exception handler}. * * @see #setDefaultUncaughtExceptionHandler * @see #setUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ public interface UncaughtExceptionHandler { /** * Method invoked when the given thread terminates due to the given * uncaught exception. * <p> * Any exception thrown by this method will be ignored by the Java * Virtual Machine. * * @param t * the thread * @param e * the exception */ void uncaughtException(Thread t, Throwable e); } // null unless explicitly set private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null unless explicitly set private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; /** * Set the default handler invoked when a thread abruptly terminates due to * an uncaught exception, and no other handler has been defined for that * thread. * * <p> * Uncaught exception handling is controlled first by the thread, then by * the thread's {@link ThreadGroup} object and finally by the default * uncaught exception handler. If the thread does not have an explicit * uncaught exception handler set, and the thread's thread group (including * parent thread groups) does not specialize its <tt>uncaughtException</tt> * method, then the default handler's <tt>uncaughtException</tt> method will * be invoked. * <p> * By setting the default uncaught exception handler, an application can * change the way in which uncaught exceptions are handled (such as logging * to a specific device, or file) for those threads that would already * accept whatever "default" behavior the system provided. * * <p> * Note that the default uncaught exception handler should not usually defer * to the thread's <tt>ThreadGroup</tt> object, as that could cause infinite * recursion. * * @param eh * the object to use as the default uncaught exception handler. * If <tt>null</tt> then there is no default handler. * * @throws SecurityException * if a security manager is present and it denies <tt> * {@link RuntimePermission} * ("setDefaultUncaughtExceptionHandler")</tt> * * @see #setUncaughtExceptionHandler * @see #getUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ public static void setDefaultUncaughtExceptionHandler( UncaughtExceptionHandler eh) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission( "setDefaultUncaughtExceptionHandler")); } defaultUncaughtExceptionHandler = eh; } /** * Returns the default handler invoked when a thread abruptly terminates due * to an uncaught exception. If the returned value is <tt>null</tt>, there * is no default. * * @since 1.5 * @see #setDefaultUncaughtExceptionHandler */ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { return defaultUncaughtExceptionHandler; } /** * Returns the handler invoked when this thread abruptly terminates due to * an uncaught exception. If this thread has not had an uncaught exception * handler explicitly set then this thread's <tt>ThreadGroup</tt> object is * returned, unless this thread has terminated, in which case <tt>null</tt> * is returned. * * @since 1.5 */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { return uncaughtExceptionHandler != null ? uncaughtExceptionHandler : group; } /** * Set the handler invoked when this thread abruptly terminates due to an * uncaught exception. * <p> * A thread can take full control of how it responds to uncaught exceptions * by having its uncaught exception handler explicitly set. If no such * handler is set then the thread's <tt>ThreadGroup</tt> object acts as its * handler. * * @param eh * the object to use as this thread's uncaught exception handler. * If <tt>null</tt> then this thread has no explicit handler. * @throws SecurityException * if the current thread is not allowed to modify this thread. * @see #setDefaultUncaughtExceptionHandler * @see ThreadGroup#uncaughtException * @since 1.5 */ public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) { checkAccess(); uncaughtExceptionHandler = eh; } /** * Dispatch an uncaught exception to the handler. This method is intended to * be called only by the JVM. */ private void dispatchUncaughtException(Throwable e) { getUncaughtExceptionHandler().uncaughtException(this, e); } /* Some private helper methods */ private native void setPriority0(int newPriority); private native void stop0(Object o); private native void suspend0(); private native void resume0(); private native void interrupt0(); }