线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、waiting、timed_waiting、终止(terminated)。
当需要新起一个线程来执行某个子任务时,就创建了一个线程。但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内存资源,譬如程序计数器、Java栈、本地方法栈都是线程私有的,所以需要为线程分配一定的内存空间),只有线程运行需要的所有条件满足了,才进入就绪状态。
当线程进入就绪状态后,不代表立刻就能获取CPU执行时间,也许此时CPU正在执行其他的事情,因此它要等待。当得到CPU执行时间之后,线程便真正进入运行状态。
线程在过程中,可能有多个原因导致当前线程不继续运行下去,从而进入waiting、timed_waiting、blocked等状态,线程状态如下:
/**
* A thread state. A thread can be in one of the following states:
*
* - {@link #NEW}
* A thread that has not yet started is in this state.
*
* - {@link #RUNNABLE}
* A thread executing in the Java virtual machine is in this state.
*
* - {@link #BLOCKED}
* A thread that is blocked waiting for a monitor lock
* is in this state.
*
* - {@link #WAITING}
* A thread that is waiting indefinitely for another thread to
* perform a particular action is in this state.
*
* - {@link #TIMED_WAITING}
* A thread that is waiting for another thread to perform an action
* for up to a specified waiting time is in this state.
*
* - {@link #TERMINATED}
* A thread that has exited is in this state.
*
*
*
*
* A thread can be in only one state at a given point in time.
* These states are virtual machine states which do not reflect
* any operating system thread states.
*
* @since 1.5
* @see #getState
*/
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
*
* - {@link Object#wait() Object.wait} with no timeout
* - {@link #join() Thread.join} with no timeout
* - {@link LockSupport#park() LockSupport.park}
*
*
* A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
*
* - {@link #sleep Thread.sleep}
* - {@link Object#wait(long) Object.wait} with timeout
* - {@link #join(long) Thread.join} with timeout
* - {@link LockSupport#parkNanos LockSupport.parkNanos}
* - {@link LockSupport#parkUntil LockSupport.parkUntil}
*
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
由Thread的源码可知,多个线程在访问synchronized同步块时没有拿到锁的线程会进入Blocked状态;当前线程调用object.wait()释放锁会进入waiting,在当前线程中调用另一个线程的join方法当前线程会进入waiting状态;调用object.wait(long timeout)、thread.join(long timeout)、sleep(long time)等方法当前线程会进入timed_waiting状态。
当由于突然中断或者任务执行完毕,线程就会消亡进入terminated状态。下面这副图描述了线程从创建到消亡之间的状态:
对于单核CPU来说,CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。
由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。比如一个线程A正在读取一个文件的内容,正读到文件的一半,此时需要暂停线程A,转去执行线程B,当再次切换回来执行线程A的时候,我们不希望线程A又从文件的开头来读取。
因此需要记录线程A的运行状态,那么会记录哪些数据呢?因为下次恢复时需要知道在这之前当前线程已经执行到哪条指令了,所以需要记录程序计数器的值;另外比如说线程正在进行某个计算的时候被挂起了,那么下次继续执行的时候需要知道之前挂起时变量的值时多少,因此需要记录CPU寄存器的状态。所以一般来说,线程上下文切换过程中会记录程序计数器、CPU寄存器状态等数据。说简单点的:对于线程的上下文切换实际上就是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。
虽然多线程可以使得任务执行的效率得到提升,但是由于在线程切换时同样会带来一定的开销代价,并且多个线程会导致系统资源占用的增加,所以在进行多线程编程时要注意这些因素。
private final Object lock = new Object();
private volatile long nativePeer;
boolean started = false;
private volatile String 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 values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
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;
/*
* Thread ID
*/
private long tid;
/* For generating thread ID */
private static long threadSeqNumber;
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
/**
* 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 final Object blockerLock = new Object();
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。
start()方法源码如下:
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the run
method of this thread.
*
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* start
method) and the other thread (which executes its
* run
method).
*
* 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".
*/
// Android-changed: throw if 'started' is true
if (threadStatus != 0 || started)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
started = false;
try {
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,JVM会为相应的线程分配需要的资源,start()调用后线程会进入runnable可运行状态。
run()方法源码如下:
/**
* If this thread was constructed using a separate
* Runnable
run object, then that
* Runnable
object's run
method is called;
* otherwise, this method does nothing and returns.
*
* Subclasses of Thread
should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
sleep()方法源码如下:
/**
* 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
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public static void sleep(long millis) throws InterruptedException {
Thread.sleep(millis, 0);
}
@FastNative
private static native void sleep(Object lock, long millis, int nanos)
throws InterruptedException;
/**
* Causes the currently executing thread to sleep (temporarily 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
* {@code 0-999999} additional nanoseconds to sleep
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value of
* {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("millis < 0: " + millis);
}
if (nanos < 0) {
throw new IllegalArgumentException("nanos < 0: " + nanos);
}
if (nanos > 999999) {
throw new IllegalArgumentException("nanos > 999999: " + nanos);
}
// The JLS 3rd edition, section 17.9 says: "...sleep for zero
// time...need not have observable effects."
if (millis == 0 && nanos == 0) {
// ...but we still have to handle being interrupted.
if (Thread.interrupted()) {
throw new InterruptedException();
}
return;
}
long start = System.nanoTime();
long duration = (millis * NANOS_PER_MILLI) + nanos;
Object lock = currentThread().lock;
// Wait may return early, so loop until sleep duration passes.
synchronized (lock) {
while (true) {
sleep(lock, millis, nanos);
long now = System.nanoTime();
long elapsed = now - start;
if (elapsed >= duration) {
break;
}
duration -= elapsed;
start = now;
millis = duration / NANOS_PER_MILLI;
nanos = (int) (duration % NANOS_PER_MILLI);
}
}
}
sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态sleep()方法来实现。当前线程调用sleep()方法进入阻塞状态后,在其睡眠时间内,该线程不会获得执行机会,即使系统中没有其他可执行线程,处于sleep()中的线程也不会执行,因此sleep()方法常用来暂停程序的执行。
但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象,如下示例:
private static final String TAG = TestThread.class.getName();
private Object object = new Object();
private int count = 1;
public void testSleep() {
new MyThread().start();
new MyThread().start();
}
class MyThread extends Thread {
@Override
public void run() {
synchronized (object) {
try {
LogUtil.log(TAG,Thread.currentThread().getName() + "开始");
count++;
LogUtil.log(TAG,count+"");
Thread.sleep(2000);
count++;
LogUtil.log(TAG,count+"");
LogUtil.log(TAG,Thread.currentThread().getName() + "结束");
} catch (Exception e) {
}
}
}
}
输出如下:
TestThread: Thread-4170开始
TestThread: 2
TestThread: 3
TestThread: Thread-4170结束
TestThread: Thread-4171开始
TestThread: 4
TestThread: 5
TestThread: Thread-4171结束
从上面输出结果可以看出,在Thread-4170休眠期间,Thread-4171并没有执行任务,在Thread-4170执行完任务释放了对象锁,Thread-4171才开始执行。
注意,如果调用了sleep方法,必须捕获InterruptedException异常或者将该异常向上层抛出,因为在线程休眠期间可以被中断。当线程睡眠时间满后,线程会进入runnable可运行状态,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。
yield()源码如下:
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
*
It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
join()方法源码如下:
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public final void join(long millis) throws InterruptedException {
synchronized(lock) {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
lock.wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
lock.wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
}
/**
* Waits at most {@code millis} milliseconds plus
* {@code nanos} nanoseconds for this thread to die.
*
*
This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @param nanos
* {@code 0-999999} additional nanoseconds to wait
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative, or the value
* of {@code nanos} is not in the range {@code 0-999999}
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public final void join(long millis, int nanos)
throws InterruptedException {
synchronized(lock) {
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.
*
*
An invocation of this method behaves in exactly the same
* way as the invocation
*
*
* {@linkplain #join(long) join}{@code (0)}
*
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
在线程A中调用线程B.join(),线程A会进入waiting状态,等线程B执行完成消亡后,线程A进入runnable可运行状态等待CPU时间片;如果在线程A中调用线程B.join(long time),A会进入waiting状态并在time时间后自动进入runnable可运行状态等待CPU时间片,而B会继续运行。示例如下:
class MyThread2 extends Thread {
@Override
public void run() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
try {
Thread.sleep(5000);
} catch (Exception e) {
}
LogUtil.log(TAG, Thread.currentThread().getName() + "结束");
}
}
public void testJoin() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
MyThread2 myThread2 = new MyThread2();
myThread2.start();
try {
//当前线程只等myThread2执行2秒,2秒后当前线程会执行后续的代码
myThread2.join(2000);
} catch (Exception e) {
LogUtil.loge(TAG,"Exception:"+e.getMessage());
}
LogUtil.log(TAG, Thread.currentThread().getName() + "结束");
}
输出如下:
TestThread: main开始
TestThread: Thread-4302开始
TestThread: main结束
TestThread: Thread-4302结束
可以看出主线程等Thread-4302执行了2秒后,继续执行;而Thread-4302运行直到结束。由上面的join方法源码可知,join的实现是通过lock.wait(0)或者lock.wait(time)实现的。主线程在调用myThread2.join()方法时,会获得lock对象的对象锁,lock对象定义如下:
private final Object lock = new Object();
等待时间millis为0的话,主线程会一直wait,并且会释放lock对象的对象锁,并交出CPU执行权限,直到myThread2运行结束后,JVM会调用lock.notify_all(thread)唤醒主线程继续执行;millis大于0的话,主线程wait的时间为millis,时间到后JVM会调用lock.notify_all(thread)唤醒主线程继续执行。
interrupt()方法源码如下:
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
nativeInterrupt();
b.interrupt(this);
return;
}
}
nativeInterrupt();
}
private native void nativeInterrupt();
调用interrupt()可以中断一个处于阻塞状态的线程,并且该线程会抛出一个中断异常,示例如下:
class MyThread2 extends Thread {
@Override
public void run() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
LogUtil.log(TAG, Thread.currentThread().getName() + "被中断");
}
LogUtil.log(TAG, Thread.currentThread().getName() + "结束");
}
}
public void testInterrupt() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
MyThread2 myThread2 = new MyThread2();
myThread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread2.interrupt();
LogUtil.log(TAG, Thread.currentThread().getName() + "结束");
}
输出如下:
main开始
Thread-4838开始
main结束
Thread-4838被中断
Thread-4838结束
interrupt()不能中断正在运行的线程,示例如下:
class MyThread2 extends Thread {
@Override
public void run() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
int i = 0;
while(i
运行该程序会发现,while循环会一直运行直到变量i的值超出Integer.MAX_VALUE,所以说直接调用interrupt方法不能中断正在运行中的线程。
但是如果配合isInterrupted()能够中断正在运行的线程,因为调用interrupt方法相当于将中断标志置为true,那么可以通过调用isInterrupted()判断中断标志是否被置位来中断线程的执行,示例如下:
class MyThread2 extends Thread {
@Override
public void run() {
LogUtil.log(TAG, Thread.currentThread().getName() + "开始");
while (!isInterrupted()) {
}
LogUtil.log(TAG, Thread.currentThread().getName() + "结束");
}
}
public void testInterrupt() {
MyThread2 myThread2 = new MyThread2();
myThread2.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
myThread2.interrupt();
}
或者手动给线程添加中断标记也可,如下:
class MyThread extends Thread {
private volatile boolean isStop = false;
@Override
public void run() {
int i = 0;
while (!isStop) {
i++;
}
}
public void setStop(boolean stop) {
this.isStop = stop;
}
}
那么就可以在外面通过调用setStop方法来终止while循环。
此处需要说明,调用thread.interrupt()只是将thread的中断标志置为true,thread还是会继续运行。isInterrupted()是Thread的对象方法,获取的是thread对象的中断标志,调用thread.interrupt()将中断标志置为true之后,再调用thread.isInterrupted()会返回true。interrupted()是Thead的静态方法,用于检测当前线程是否已经被中断,第一次调用interrupted()时如果当前线程已经被中断,interrupted()返回true,当前线程没有被中断interrupted()会返回false;第二次再调用interrupted()时中断状态已经被清除,将返回一个false。示例可以参考:https://blog.csdn.net/qq_39682377/article/details/81449451
以下是关系到线程属性的几个方法:
1)getId
用来得到线程ID
2)getName和setName
用来得到或者设置线程名称。
3)getPriority和setPriority
用来获取和设置线程优先级。
4)setDaemon和isDaemon
用来设置线程是否成为守护线程和判断线程是否是守护线程。
守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。
Thread类有一个比较常用的静态方法currentThread()用来获取当前线程。