/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.lang; import dalvik.system.VMStack; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import libcore.util.EmptyArray; /** * A {@code Thread} is a concurrent unit of execution. It has its own call stack * for methods being invoked, their arguments and local variables. Each application * has at least one thread running when it is started, the main thread, in the main * {@link ThreadGroup}. The runtime keeps its own threads in the system thread * group. * *

There are two ways to execute code in a new thread. * You can either subclass {@code Thread} and overriding its {@link #run()} method, * or construct a new {@code Thread} and pass a {@link Runnable} to the constructor. * In either case, the {@link #start()} method must be called to actually execute * the new {@code Thread}. * *

Each {@code Thread} has an integer priority that affect how the thread is * scheduled by the OS. A new thread inherits the priority of its parent. * A thread's priority can be set using the {@link #setPriority(int)} method. */ public class Thread implements Runnable { private static final int NANOS_PER_MILLI = 1000000; /** Park states */ private static class ParkState { /** park state indicating unparked */ private static final int UNPARKED = 1; /** park state indicating preemptively unparked */ private static final int PREEMPTIVELY_UNPARKED = 2; /** park state indicating parked */ private static final int PARKED = 3; } /** * A representation of a thread's state. A given thread may only be in one * state at a time. */ public enum State { /** * The thread has been created, but has never been started. */ NEW, /** * The thread may be run. */ RUNNABLE, /** * The thread is blocked and waiting for a lock. */ BLOCKED, /** * The thread is waiting. */ WAITING, /** * The thread is waiting for a specified amount of time. */ TIMED_WAITING, /** * The thread has been terminated. */ TERMINATED } /** * The maximum priority value allowed for a thread. * This corresponds to (but does not have the same value as) * {@code android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY}. */ public static final int MAX_PRIORITY = 10; /** * The minimum priority value allowed for a thread. * This corresponds to (but does not have the same value as) * {@code android.os.Process.THREAD_PRIORITY_LOWEST}. */ public static final int MIN_PRIORITY = 1; /** * The normal (default) priority value assigned to the main thread. * This corresponds to (but does not have the same value as) * {@code android.os.Process.THREAD_PRIORITY_DEFAULT}. */ public static final int NORM_PRIORITY = 5; /* some of these are accessed directly by the VM; do not rename them */ volatile VMThread vmThread; volatile ThreadGroup group; volatile boolean daemon; volatile String name; volatile int priority; volatile long stackSize; Runnable target; private static int count = 0; /** * Holds the thread's ID. We simply count upwards, so * each Thread has a unique ID. */ private long id; /** * Normal thread local values. */ ThreadLocal.Values localValues; /** * Inheritable thread local values. */ ThreadLocal.Values inheritableValues; /** Callbacks to run on interruption. */ private final List interruptActions = new ArrayList(); /** * Holds the class loader for this Thread, in case there is one. */ private ClassLoader contextClassLoader; /** * Holds the handler for uncaught exceptions in this Thread, * in case there is one. */ private UncaughtExceptionHandler uncaughtHandler; /** * Holds the default handler for uncaught exceptions, in case there is one. */ private static UncaughtExceptionHandler defaultUncaughtHandler; /** * Reflects whether this Thread has already been started. A Thread * can only be started once (no recycling). Also, we need it to deduce * the proper Thread status. */ boolean hasBeenStarted = false; /** the park state of the thread */ private int parkState = ParkState.UNPARKED; /** The synchronization object responsible for this thread parking. */ private Object parkBlocker; /** * Constructs a new {@code Thread} with no {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the same * {@code ThreadGroup} as the {@code Thread} calling this constructor. * * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread() { create(null, null, null, 0); } /** * Constructs a new {@code Thread} with a {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the same * {@code ThreadGroup} as the {@code Thread} calling this constructor. * * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(Runnable runnable) { create(null, runnable, null, 0); } /** * Constructs a new {@code Thread} with a {@code Runnable} object and name * provided. The new {@code Thread} will belong to the same {@code * ThreadGroup} as the {@code Thread} calling this constructor. * * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * @param threadName * the name for the {@code Thread} being created * * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(Runnable runnable, String threadName) { if (threadName == null) { throw new NullPointerException("threadName == null"); } create(null, runnable, threadName, 0); } /** * Constructs a new {@code Thread} with no {@code Runnable} object and the * name provided. The new {@code Thread} will belong to the same {@code * ThreadGroup} as the {@code Thread} calling this constructor. * * @param threadName * the name for the {@code Thread} being created * * @see java.lang.ThreadGroup * @see java.lang.Runnable * */ public Thread(String threadName) { if (threadName == null) { throw new NullPointerException("threadName == null"); } create(null, null, threadName, 0); } /** * Constructs a new {@code Thread} with a {@code Runnable} object and a * newly generated name. The new {@code Thread} will belong to the {@code * ThreadGroup} passed as parameter. * * @param group * {@code ThreadGroup} to which the new {@code Thread} will * belong * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * @throws IllegalThreadStateException * if group.destroy() has already been done * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(ThreadGroup group, Runnable runnable) { create(group, runnable, null, 0); } /** * Constructs a new {@code Thread} with a {@code Runnable} object, the given * name and belonging to the {@code ThreadGroup} passed as parameter. * * @param group * ThreadGroup to which the new {@code Thread} will belong * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * @param threadName * the name for the {@code Thread} being created * @throws IllegalThreadStateException * if group.destroy() has already been done * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(ThreadGroup group, Runnable runnable, String threadName) { if (threadName == null) { throw new NullPointerException("threadName == null"); } create(group, runnable, threadName, 0); } /** * Constructs a new {@code Thread} with no {@code Runnable} object, the * given name and belonging to the {@code ThreadGroup} passed as parameter. * * @param group * {@code ThreadGroup} to which the new {@code Thread} will belong * @param threadName * the name for the {@code Thread} being created * @throws IllegalThreadStateException * if group.destroy() has already been done * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(ThreadGroup group, String threadName) { if (threadName == null) { throw new NullPointerException("threadName == null"); } create(group, null, threadName, 0); } /** * Constructs a new {@code Thread} with a {@code Runnable} object, the given * name and belonging to the {@code ThreadGroup} passed as parameter. * * @param group * {@code ThreadGroup} to which the new {@code Thread} will * belong * @param runnable * a {@code Runnable} whose method run will be * executed by the new {@code Thread} * @param threadName * the name for the {@code Thread} being created * @param stackSize * a stack size for the new {@code Thread}. This has a highly * platform-dependent interpretation. It may even be ignored * completely. * @throws IllegalThreadStateException * if group.destroy() has already been done * @see java.lang.ThreadGroup * @see java.lang.Runnable */ public Thread(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { if (threadName == null) { throw new NullPointerException("threadName == null"); } create(group, runnable, threadName, stackSize); } /** * Package-scope method invoked by Dalvik VM to create "internal" * threads or attach threads created externally. * * Don't call Thread.currentThread(), since there may not be such * a thing (e.g. for Main). */ Thread(ThreadGroup group, String name, int priority, boolean daemon) { synchronized (Thread.class) { id = ++Thread.count; } if (name == null) { this.name = "Thread-" + id; } else { this.name = name; } if (group == null) { throw new InternalError("group not specified"); } this.group = group; this.target = null; this.stackSize = 0; this.priority = priority; this.daemon = daemon; /* add ourselves to our ThreadGroup of choice */ this.group.addThread(this); } /** * Initializes a new, existing Thread object with a runnable object, * the given name and belonging to the ThreadGroup passed as parameter. * This is the method that the several public constructors delegate their * work to. * * @param group ThreadGroup to which the new Thread will belong * @param runnable a java.lang.Runnable whose method run will * be executed by the new Thread * @param threadName Name for the Thread being created * @param stackSize Platform dependent stack size * @throws IllegalThreadStateException if group.destroy() has * already been done * @see java.lang.ThreadGroup * @see java.lang.Runnable */ private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) { Thread currentThread = Thread.currentThread(); if (group == null) { group = currentThread.getThreadGroup(); } if (group.isDestroyed()) { throw new IllegalThreadStateException("Group already destroyed"); } this.group = group; synchronized (Thread.class) { id = ++Thread.count; } if (threadName == null) { this.name = "Thread-" + id; } else { this.name = threadName; } this.target = runnable; this.stackSize = stackSize; this.priority = currentThread.getPriority(); this.contextClassLoader = currentThread.contextClassLoader; // Transfer over InheritableThreadLocals. if (currentThread.inheritableValues != null) { inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues); } // add ourselves to our ThreadGroup of choice this.group.addThread(this); } /** * Returns the number of active {@code Thread}s in the running {@code * Thread}'s group and its subgroups. * * @return the number of {@code Thread}s */ public static int activeCount() { return currentThread().getThreadGroup().activeCount(); } /** * Does nothing. */ public final void checkAccess() { } /** * Returns the number of stack frames in this thread. * * @return Number of stack frames * @deprecated The results of this call were never well defined. To make * things worse, it would depend on whether the Thread was * suspended or not, and suspend was deprecated too. */ @Deprecated public int countStackFrames() { return getStackTrace().length; } /** * Returns the Thread of the caller, that is, the current Thread. * * @return the current Thread. */ public static Thread currentThread() { return VMThread.currentThread(); } /** * Throws {@code UnsupportedOperationException}. * @deprecated Not implemented. */ @Deprecated public void destroy() { throw new UnsupportedOperationException(); } /** * Prints to the standard error stream a text representation of the current * stack for this Thread. * * @see Throwable#printStackTrace() */ public static void dumpStack() { new Throwable("stack dump").printStackTrace(); } /** * Copies an array with all Threads which are in the same ThreadGroup as the * receiver - and subgroups - into the array threads passed as * parameter. If the array passed as parameter is too small no exception is * thrown - the extra elements are simply not copied. * * @param threads * array into which the Threads will be copied * @return How many Threads were copied over */ public static int enumerate(Thread[] threads) { Thread thread = Thread.currentThread(); return thread.getThreadGroup().enumerate(threads); } /** * Returns a map of all the currently live threads to their stack traces. */ public static Map getAllStackTraces() { Map map = new HashMap(); // Find out how many live threads we have. Allocate a bit more // space than needed, in case new ones are just being created. int count = ThreadGroup.mSystem.activeCount(); Thread[] threads = new Thread[count + count / 2]; // Enumerate the threads and collect the stacktraces. count = ThreadGroup.mSystem.enumerate(threads); for (int i = 0; i < count; i++) { map.put(threads[i], threads[i].getStackTrace()); } return map; } /** * Returns the context ClassLoader for this Thread. * * @return ClassLoader The context ClassLoader * @see java.lang.ClassLoader * @see #getContextClassLoader() */ public ClassLoader getContextClassLoader() { return contextClassLoader; } /** * Returns the default exception handler that's executed when uncaught * exception terminates a thread. * * @return an {@link UncaughtExceptionHandler} or null if * none exists. */ public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() { return defaultUncaughtHandler; } /** * Returns the thread's identifier. The ID is a positive long * generated on thread creation, is unique to the thread, and doesn't change * during the lifetime of the thread; the ID may be reused after the thread * has been terminated. * * @return the thread's ID. */ public long getId() { return id; } /** * Returns the name of the Thread. */ public final String getName() { return name; } /** * Returns the priority of the Thread. */ public final int getPriority() { return priority; } /** * Returns an array of {@link StackTraceElement} representing the current thread's stack. */ public StackTraceElement[] getStackTrace() { StackTraceElement ste[] = VMStack.getThreadStackTrace(this); return ste != null ? ste : EmptyArray.STACK_TRACE_ELEMENT; } /** * Returns the current state of the Thread. This method is useful for * monitoring purposes. * * @return a {@link State} value. */ public State getState() { // TODO This is ugly and should be implemented better. VMThread vmt = this.vmThread; // Make sure we have a valid reference to an object. If native code // deletes the reference we won't run into a null reference later. VMThread thread = vmThread; if (thread != null) { // If the Thread Object became invalid or was not yet started, // getStatus() will return -1. int state = thread.getStatus(); if(state != -1) { return VMThread.STATE_MAP[state]; } } return hasBeenStarted ? Thread.State.TERMINATED : Thread.State.NEW; } /** * Returns the ThreadGroup to which this Thread belongs. * * @return the Thread's ThreadGroup */ public final ThreadGroup getThreadGroup() { // TODO This should actually be done at native termination. if (getState() == Thread.State.TERMINATED) { return null; } else { return group; } } /** * Returns the thread's uncaught exception handler. If not explicitly set, * then the ThreadGroup's handler is returned. If the thread is terminated, * then null is returned. * * @return an {@link UncaughtExceptionHandler} instance or {@code null}. */ public UncaughtExceptionHandler getUncaughtExceptionHandler() { if (uncaughtHandler != null) return uncaughtHandler; else return group; // ThreadGroup is instance of UEH } /** * Posts an interrupt request to this {@code Thread}. The behavior depends on * the state of this {@code Thread}: *