The source of this article Thread thread knowledge point explanation reproduced please indicate the source
Internal properties
//线程名,如果创建时没有指定则使用Thread- + 创建序列号
private volatile String name;
//线程优先级 Java只是给操作系统一个优先级的参考值,线程最终在操作系统的优先级是多少还是由操作系统决定。
private int priority;
//守护线程
private boolean daemon = false;
//为JVM保留字段
private boolean stillborn = false;
private long eetop;
/* What will be run. */
private Runnable target;
//线程组,每一个线程必定存于一个线程组中,线程不能独立于线程组外
private ThreadGroup group;
// 类加载器,当线程需要加载类时,会使用内部类加器
private ClassLoader contextClassLoader;
/* 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 final long stackSize;
/*
* JVM-private state that persists after native thread termination.
*/
private long nativeParkEventPointer;
/*
* Thread ID
*/
private final long tid;
/* For generating thread ID */
private static long threadSeqNumber;
// 这个线程号是整个Thread 类共享的
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
/*
* 线程状态
*/
private volatile int threadStatus;
Constructor
public Thread() {
this(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
Thread parent = currentThread(); //从创建Thread 的线程中获取到父线程
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security manager doesn't have a strong opinion
on the matter, use the parent thread group. */
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(
SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
//对没有启动线程进行计数
g.addUnstarted();
this.group = g;
//如果在创建线程时没有设置守护线程,优先级、类加器这些,全部都是当前现场的
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && 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 */
this.tid = nextThreadID();
}
The construction method actually initializes the internal properties of Thread, such as thread name, priority, class adder, thread Id. If not set these properties are all inherited from the current one. What surprises me is that it is very important that threadStatus
does not assign a value, but uses the default value. I guess this variable is changed by C++ throughout the whole process, so there is no need to use Java for assignment.
The initialized thread object can use the set method to modify the daemon thread, thread name, and priority.
thread state
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:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code 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:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
Thread status is often asked in interviews, and everyone has a note on several statuses and their meanings.
condition | describe | Scenes |
---|---|---|
NEW | Thread thread has just been created, creating state | new Thread |
RUNNABLE | Running state, the thread is running | Thread.start |
BLOCKED | blocked state | synchronized race failed |
WAITING | Wait, this state either waits indefinitely or is awakened | Object.wait、Lock |
TIMED_WAITING | Waiting for a timeout, set the time while waiting, and automatically wake up when the time comes | Thread.sleep、LockSupport.parkNanos |
TERMINATED | state of death | The thread has completed its task |
As can be seen from the figure below, the process from create -> run -> death is irreversible.
Thread running and stopping
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) //状态必须是创建状态 NEW ,防止一个对象多次调用start 方法
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); //加入线程组容器中,未开始线程数-1
boolean started = false;
try {
start0();
started = true;
} finally {
try {
// 进入到这里,则start0 创建一个线程失败了,要从线程组中删除它,未开始线程再加回来
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 void start0();
start
method is relatively simple, first determine whether the status is correct, add it to the thread group before creation, and then remove it if it fails. The start0 method should call the system resource to actually create a thread, and the thread state is also modified by this method.
run
method only uses Thread to create a thread and uses Runnable to pass parameters to execute the run method. The inheritance method should be to directly call the subclass run method.
public void run() {
if (target != null) { //有传入Runnable 对象,则调用该对象实现run方法
target.run();
}
}
Although the stop
method has been officially disabled in Java 2, it is worth understanding.
@Deprecated(since="1.2")
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
checkAccess();
if (this != Thread.currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
// A zero status value corresponds to "NEW", it can't change to
// not-NEW because we hold the lock.
if (threadStatus != 0) { //不是NEW,线程已经运行了,如果被挂起了,需要对它进行唤醒
resume(); // Wake up thread if it was suspended; no-op otherwise
}
// The VM can handle all thread states
stop0(new ThreadDeath()); //停止线程,并且抛出一个异常给JVM
}
private native void stop0(Object o);
After reading this method, I can't see what stop()
can do. I don't know what this stop can do. I will write a few examples to verify the function.
Create several threads to perform the next task. After a while, call the stop method on all threads to see if the task will exit.
public class ThreadStopTest {
public static void main(String[] args) {
ThreadStopTest t = new ThreadStopTest();
Runnable r = () -> {
int i = 0;
while (i < 1000){
t.spinMills(500);
System.out.println(Thread.currentThread().getName() + " : " + i);
i++;
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
t1.start();
t2.start();
t3.start();
t.spinMills(2000);
t1.stop();
t2.stop();
t3.stop();
}
public void spinMills(long millisecond){
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < millisecond){
//自旋 ,模拟执行任务
}
}
}
Results of the
Thread-1 : 0
Thread-0 : 0
Thread-2 : 0
Thread-1 : 1
Thread-0 : 1
Thread-2 : 1
Thread-2 : 2
Thread-1 : 2
Thread-0 : 2
After calling the stop method, the thread immediately exits the task without even throwing an exception, which is really very straightforward. If someone does not bother to use the stop method, it is very difficult to rule out any problems, so officials using it early.
If you want to stop a running thread gracefully, the official recommendation is to use interrupted()
. Thread interruption means that the target thread sends an interrupt signal, and the thread that can receive the interrupt signal implements its own exit logic. To put it simply, thread A is working, and suddenly someone makes an action on it. Thread A knows the meaning of this action, and it will know that it needs to stop. To put it bluntly, this is an action. If the thread logic does not process this action code, the thread will not exit. Take a look at the methods in the Thread class.
method | Remark |
---|---|
interrupt() | Interrupt the target thread, send an interrupt signal to the target thread, and the thread is marked as interrupted |
isInterrupted() | Determine whether the target thread is interrupted, the interrupt flag will not be cleared |
interrupted | To determine whether the target thread is interrupted, the interrupt flag will be cleared |
Implement a simple example
public static void main(String[] args) throws InterruptedException {
Runnable r = () -> {
while (!Thread.currentThread().isInterrupted()){
//do some
System.out.println(System.currentTimeMillis());
}
System.out.println("线程准备退出啦");
Thread.interrupted();
};
Thread t = new Thread(r);
t.start();
Thread.sleep(1000);
t.interrupt();
}
The core of the above code is the interrupt state. If the interrupt is cleared, the program will not jump out of the while loop. Let’s change it and add a sleep method.
public static void main(String[] args) throws InterruptedException {
Runnable r = () -> {
while (!Thread.currentThread().isInterrupted()){
//do some
try {
Thread.sleep(400);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis());
}
System.out.println("线程准备退出啦");
Thread.interrupted();
};
Thread t = new Thread(r);
t.start();
Thread.sleep(1000);
t.interrupt();
}
Execution result: After sending the interrupt, Thread.sleep directly throws an exception and does not jump out of the loop.
Because sleep will respond to the interrupt, throw an interrupt exception, and then clear the thread interrupt status. When returning to the while judgment, the interrupt state has been cleared, and the loop continues.
sleep()
is a static native method that makes the currently executing thread sleep for a specified time, but the sleeping thread will not give up the monitor's lock (synchronized), when any thread wants to interrupt the current thread, it will throw InterruptedException
exception and clean up The interrupt status of the current thread. So this exception is thrown on the method call, allowing the caller to handle the interrupt exception.
join and yield methods
join()
is a waiting method. After waiting for the current thread task to execute, wake up the called thread again, which is often used to control the execution order of multi-threaded tasks.
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* <p> 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
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) { //这里获取线程状态,只是不是开始和死亡就算alive了
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0); //在指定时间内沉睡
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
If you want to know the method, you can mainly look at the method annotation. Wait for the callee's thread to die within the specified time. If there is no dead time, it will wake up by itself. If the time is 0, it will wait forever until the execution thread finishes executing the task. The wakeup is performed by notifyAll, but I don't see where this method is performed. After checking the information, I know that the exit() method will be called after each thread is executed, and notifyAll will be called on exit.
yield()
: The word translated means concession. Main function When the thread obtains the right to execute, calling this method will actively give up the executor. It is different from the above wait and sleep. The thread state does not change, and it is still RUN at this time. For example, if a thread fails to acquire the lock, the thread cannot do anything at this time, and the acquisition of the lock itself is very fast. At this time, the thread is suspended, which is a bit more than the gain. It is better to give up the CPU executor at this time and let other threads execute it. It does not waste precious CPU time, nor does it require too much performance. This method is often used for synchronization methods in the java.util.concurrent.locks
package, and students who have seen concurrent tools should all know it.
collaboration between threads
The wait method puts the current thread into the waiting state (WAITING) and releases the monitoring lock. Only when other threads call notify or notifyAll will the thread wake up.
notify wakes up a thread in the waiting state and re-enters the RUNNABLE state.
notifyAll wakes up all threads in the waiting state and re-enters the RUNNABLE state.
The above three methods must be used under the monitoring lock (synchronized), otherwise an IllegalMonitorStateException will be thrown.
The combination of wait and notify can realize cooperation between threads. For example, the most classic producer-consumer model: When the upstream consumer sends too many messages, the queue is full, and the consumer can use wait to stop the producer. Consumption has started. At this time, the queue has been consumed with a message, and there is space. The consumer can call notify to let the upstream producer continue to operate. When the information in the queue has been consumed, the consumer will call wait to let the thread enter the waiting. When the upstream thread has information sent to the queue, the information in the queue is not completely empty at this time, you can call wait to wake up a wait for the consumer. In this way, the effect of mutual communication between threads can be formed.
Simple implementation of the consumer-producer model
public void push(T t){
synchronized (lock){
size++;
if (size == QUEUE_CAPACTIY) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
//入队列中
}
}
public T poll(){
synchronized (lock){
size--;
if (size == 0) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lock.notify();
return T;
}
}
Callable and Thread relationship
We know that all threads are actually created by Thread.start, and the run method is rewritten to achieve abnormal execution tasks, but whether the Callable interface also uses the Thread or Runnable interface, mainly depends FutureTask
to know how to implement it.
Look at the run method
public void run() {
//如果线程已经被创建了,则不需要再次执行任务了
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable; //callable 方法实现类
if (c != null && state == NEW) { //刚刚初始化的状态
V result;
boolean ran;
try {
result = c.call(); //执行任务
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex); //保存异常,将等待队列的线程全部唤醒过来
}
if (ran)
set(result); //保存执行结果,将等待队列的线程全部唤醒过来
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
It can be seen that Callable still uses Thread to create threads, and internally determines the task status by maintaining state, executes the call method in the run method, and saves exceptions and execution results.
Let's see how get() gets the execution result.
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING) //还在执行中
s = awaitDone(false, 0L); //等待任务执行完成或者中断,会堵塞调用线程
return report(s);
}
/**
* Awaits completion or aborts on interrupt or timeout.
*
* @param timed true if use timed waits
* @param nanos time to wait, if timed
* @return state upon completion or at timeout
*/
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// The code below is very delicate, to achieve these goals:
// - call nanoTime exactly once for each call to park
// - if nanos <= 0L, return promptly without allocation or nanoTime
// - if nanos == Long.MIN_VALUE, don't underflow
// - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
// and we suffer a spurious wakeup, we will do no worse than
// to park-spin for a while
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
for (;;) {
int s = state;
if (s > COMPLETING) { //如果状态已经有执行中变成其他 ,直接将状态返回
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) //正在执行中,让出CPU执行权,而不是变换线程状态
// We may have already promised (via isDone) that we are done
// so never return empty-handed or throw InterruptedException
Thread.yield();
else if (Thread.interrupted()) { //处理线程中断,退出自旋
removeWaiter(q); //删除队列中的线程
throw new InterruptedException();
}
else if (q == null) {
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
}
else if (!queued) //将等待结果线程放入一个队列中,其实这个队列就是来处理等待结果线程的中断的
queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) { // first time
startTime = System.nanoTime();
if (startTime == 0L)
startTime = 1L;
parkNanos = nanos;
} else {
long elapsed = System.nanoTime() - startTime;
if (elapsed >= nanos) {
removeWaiter(q);
return state;
}
parkNanos = nanos - elapsed;
}
// nanoTime may be slow; recheck before parking
if (state < COMPLETING) //任务没有启动,挂起等待线程
LockSupport.parkNanos(this, parkNanos);
}
else
LockSupport.park(this); //任务没有开始,挂起调用者,任务完成后会将它唤醒的
}
}
Now it is basically clear, use run to call the call method to save the execution result, and then the get method uses the spin method to wait for the execution result, and uses the queue to save the waiting thread to handle the wake-up and interruption of the thread.
Summarize
Here is a brief introduction to the construction method and attribute setting of Thread. More important is the introduction of several states of the thread, state flow, thread start and stop, interrupt processing, and several common methods. Briefly talk about the implementation principle of FutureTask
, combined with the above-mentioned knowledge points, the above-mentioned knowledge is very important, you can see that most Java concurrent classes are developed using this knowledge, which frequently appear in It is also understandable in interviews.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。