1. 写在前面
Java 中的 Thread 类是多线程编程的基础,也是我们日常工作中用的比较多的类,但是你真的了解它吗?下面这几个问题你是否有思考过?
- start() 和 run() 方法有什么区别?
- 什么是线程的生命周期?
- 什么是守护线程(Daemon Thread)?
- 如何中断线程?
- 什么是线程优先级?
- 什么是线程组(ThreadGroup)?
- 什么是线程本地变量(ThreadLocal)?
- 什么是死锁?如何避免
2. 全局视角
2.1 Runnable 接口
- 定义了一个方法 run(),需要实现该接口的类提供具体的任务执行逻辑。
- Thread 类通过实现 Runnable 接口,使得线程对象可以直接执行任务。
public interface Runnable {
public abstract void run();
}
2.2 Thread 类的部分重要方法
- start():启动线程,调用 run() 方法。
- run():线程执行的任务逻辑,通常需要重写。
- join():等待线程终止。
- sleep(long millis):使当前线程休眠指定的毫秒数。
- interrupt():中断线程。
- isInterrupted():检查线程是否被中断。
- setDaemon(boolean on):将线程设置为守护线程
- getPriority() 和 setPriority(int newPriority):获取和设置线程的优先级。
- getName() 和 setName(String name):获取和设置线程的名称。
- getState():获取线程的状态。
2.3 Thread 类的构造方法
Thread 类提供了多种构造方法,以便灵活创建线程对象。常见的构造方法包括:
- Thread()
- Thread(Runnable target)
- Thread(Runnable target, String name)
- Thread(String name)
- Thread(ThreadGroup group, Runnable target)
- Thread(ThreadGroup group, Runnable target, String name)
下面我们来详细介绍下各个构造方法是如何使用的。
3. 从使用说起
3.1 无参构造方法
无参构造方法创建一个新的线程对象,但不指定任何任务或线程名称。
public class Main {
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
System.out.println("Thread running using no-arg constructor");
}
};
thread.start();
}
}
3.2 使用 Runnable 作为参数的构造方法
这个构造方法接受一个实现了 Runnable 接口的对象作为参数,用于指定线程要执行的任务。
public class Main {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Thread running using Runnable constructor");
}
};
Thread thread = new Thread(task);
thread.start();
}
}
3.3 使用 Runnable 和 String 作为参数的构造方法
这个构造方法接受一个实现了 Runnable 接口的对象和一个线程名称,用于指定线程要执行的任务和线程的名称。
public class Main {
public static void main(String[] args) {
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Thread running using Runnable and String constructor");
}
};
Thread thread = new Thread(task, "MyThread");
thread.start();
System.out.println("Thread Name: " + thread.getName());
}
}
3.4 使用 String 作为参数的构造方法
这个构造方法接受一个线程名称,用于指定线程的名称。
public class Main {
public static void main(String[] args) {
Thread thread = new Thread("MyThread") {
@Override
public void run() {
System.out.println("Thread running using String constructor");
}
};
thread.start();
System.out.println("Thread Name: " + thread.getName());
}
}
3.5 使用 ThreadGroup 和 Runnable 作为参数的构造方法
这个构造方法接受一个线程组和一个实现了 Runnable 接口的对象,用于指定线程所属的线程组和要执行的任务。
public class Main {
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("MyGroup");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Thread running using ThreadGroup and Runnable constructor");
}
};
Thread thread = new Thread(group, task);
thread.start();
System.out.println("Thread Group: " + thread.getThreadGroup().getName());
}
}
3.6 使用 ThreadGroup, Runnable, 和 String 作为参数的构造方法
这个构造方法接受一个线程组、一个实现了 Runnable 接口的对象和一个线程名称,用于指定线程所属的线程组、要执行的任务和线程的名称。
public class Main {
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("MyGroup");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Thread running using ThreadGroup, Runnable, and String constructor");
}
};
Thread thread = new Thread(group, task, "MyThread");
thread.start();
System.out.println("Thread Group: " + thread.getThreadGroup().getName());
System.out.println("Thread Name: " + thread.getName());
}
}
3.7 使用 ThreadGroup, Runnable, String, 和 long 作为参数的构造方法
这个构造方法接受一个线程组、一个实现了 Runnable 接口的对象、一个线程名称和一个栈大小,用于指定线程所属的线程组、要执行的任务、线程的名称和线程的栈大小。
public class Main {
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("MyGroup");
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Thread running using ThreadGroup, Runnable, String, and long constructor");
}
};
Thread thread = new Thread(group, task, "MyThread", 1024);
thread.start();
System.out.println("Thread Group: " + thread.getThreadGroup().getName());
System.out.println("Thread Name: " + thread.getName());
}
}
4. start()方法的底层源码
上面的使用中我们发现start()是用的比较多的一个方法,下面我们就深入看看它的源码实现,源码如下:
/**
* 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();
/* 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);
boolean started = false;
try {
start0();
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 */
}
}
}
start() 方法用于启动一个新线程,使其调用 run() 方法并在新的线程中执行。这个方法是线程启动的核心部分,确保线程正确地从 “NEW” 状态转变到 “RUNNABLE” 状态。下面是对这个方法的逐步解析:
4.1 方法签名
public synchronized void start() {
- public:表示这个方法是公有的,可以从类的外部调用。
- synchronized:表示这个方法是同步的,确保同一时间只有一个线程可以执行该方法,防止并发问题。
4.2 方法实现
/**
* 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".
*/
这段注释说明了 start() 方法的一些背景信息:
- 这个方法不会被主线程或由虚拟机创建的 “系统” 线程调用。
- 任何将来添加到这个方法的新功能也可能需要添加到虚拟机中。
- threadStatus 为 0 表示线程处于 “NEW” 状态。
if (threadStatus != 0)
throw new IllegalThreadStateException();
- 这段代码检查线程的状态。如果 threadStatus 不是 0(即线程不是处于 “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);
- 这段代码通知线程组(ThreadGroup)该线程即将启动。线程组会将该线程添加到其线程列表中,并减少未启动线程的计数。
boolean started = false;
try {
start0();
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 */
}
}
- boolean started = false;:初始化一个布尔变量 started 为 false,用于跟踪线程是否成功启动。
- start0();:调用本地方法 start0(),这是一个本地方法,用于启动新线程。start0() 是一个私有的本地方法,由 JVM 实现,用来执行底层的线程启动操作。
- started = true;:如果 start0() 成功执行,将 started 设置为 true。
4.3 finally 块
- finally 块确保在任何情况下都能执行清理操作。
- 如果 start0() 方法抛出异常且 started 仍然为 false,则调用 group.threadStartFailed(this); 通知线程组线程启动失败。
- catch (Throwable ignore):捕获所有异常,并忽略它们。这是为了确保如果 start0() 抛出异常,它会被传递到调用栈上,而不会被这个捕获块阻止。
5. run()方法
/**
* 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)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
run()方法的源代码如上,看起来很简单,run() 方法是线程执行的入口点。当一个线程启动后,JVM 会调用该线程的 run() 方法。这个方法的默认实现检查线程是否有一个关联的 Runnable 对象,如果有,则调用该 Runnable 对象的 run() 方法。
5.1 方法签名和注释
/**
* 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)
*/
@Override
public void run() {
- 如果该线程是用一个单独的 Runnable 对象构造的,那么会调用这个 Runnable 对象的 run() 方法;否则,这个方法什么也不做,直接返回。
- Thread 类的子类应该重写这个方法,以提供具体的执行逻辑。
- 提供了一些相关方法的链接,包括 start()、stop() 和 Thread(ThreadGroup, Runnable, String)。
5.2 方法实现
if (target != null) {
target.run();
}
}
-
if (target != null)
- 检查 target 是否为 null。target 是一个 Runnable 对象,表示线程要执行的任务。
- target 是 Thread 类的一个实例变量,当使用 Thread(Runnable target) 构造方法创建线程时,会将传入的 Runnable 对象赋值给 target。
-
target.run()
- 如果 target 不为 null,则调用 target 的 run() 方法。这意味着如果线程是用一个 Runnable 对象创建的,那么实际执行的是这个 Runnable 对象的 run() 方法
- 如果 target 为 null,则什么也不做,直接返回
5.3 例子
下面是一个使用 Thread 类和 Runnable 接口的示例,展示了如何使用 run() 方法:
5.3.1 使用 Runnable 接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Runnable run method executed.");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start(); // This will call myRunnable's run() method
}
}
在这个例子中,MyRunnable 实现了 Runnable 接口,并重写了 run() 方法。创建 Thread 对象时,将 MyRunnable 对象传递给 Thread 的构造方法。调用 thread.start() 时,最终会调用 MyRunnable 对象的 run() 方法。
5.3.2 继承 Thread 类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Thread run method executed.");
}
}
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start(); // This will call MyThread's run() method
}
}
在这个例子中,MyThread 继承了 Thread 类,并重写了 run() 方法。创建 MyThread 对象并调用 myThread.start() 时,会执行 MyThread 类的 run() 方法。
系列文章
7.jdk源码阅读之ConcurrentHashMap(上)
8.jdk源码阅读之ConcurrentHashMap(下)
17.jdk源码阅读之LinkedBlockingQueue