目录
[1、继承 Thread 类](#1、继承 Thread 类)
[2、实现 Runnable 接口](#2、实现 Runnable 接口)
[4、sleep(long millis)](#4、sleep(long millis))
前言
计算机早期程序是按顺序执行的
一个任务完成后才开始下一个任务
想象一下
当你在网页中下载东西时
整个页面就会冻结
不能进行其他操作
随着硬件性能提升和人们的需求增长
大家开始思考:
如何让多个任务同时进行
这便催生了多线程编程的概念
Java从诞生之初就将多线程作为核心特性之一
一、进程与线程
在介绍线程之前,我们先来学习一下进程,它们二者有着密不可分的联系:
1、进程
进程指一个内存中运行的应用程序,它是系统运行程序的基本单位
一个程序从创建、运行到消亡,这样整个过程就是一个进程
一个操作系统中可以同时运行多个进程,每个进程运行时,系统都会为其分配独立的内存空间

2、线程
线程是进程中的一个执行单元,负责当前进程中程序的执行
是操作系统能够进行运算调度的最小单位
总结一下:
进程是资源分配的最小单位
线程是CPU调度的最小单位
一个程序运行后至少有一个进程
一个进程中至少包含一个线程(main线程)或多个线程
当一个进程中启动了多个线程
这个程序就是多线程程序
JVM是多线程的吗?
是的,JVM在运行程序的同时,进行GC垃圾回收
二、并发与并行
1、并发
指两个或多个事件在同一时间段内 发生
线程的并发执行,是指在一个时间段内(微观,转瞬即逝)
两个或多个线程,使用同一个CPU交替运行

2、并行
指两个或多个事件在同一时刻发生(同时发生)
现成的并行执行,是指在同一时刻
两个或多个线程,各自使用一个CPU同时运行

单核CPU计算机,同一时刻只能有一个线程
多核CPU计算机,同一时刻可能有多个线程
计算机内有专门的资源调度算法
所以我们从程序层面无法得知也无法干涉具体用几个CPU运行
三、线程调度
针对于并发多线程( 只有一个CPU**)** 讨论**:**
1、CPU时间片
发明时间片机制的原因:
- 为了防止一个线程一直占用CPU,保证系统响应速度
- 实现多任务并发执行,让多个线程看起来是同时运行的
- 通过时间片轮转调度算法实现公平公正的CPU资源分配
CPU时间片(Time Slice)
是操作系统分配给每个线程或进程的CPU执行时间段
是现代操作系统实现多任务处理的核心机制
由于这个时间段一般是微秒纳秒级别
所以宏观上我们会感觉多个线程在同时运行代码
其实微观中它们是交替运行的
只不过交替的速度极快
2、调度方式
常见调度方式:
①时间片轮转
为每个就绪进程分配一个固定长度的时间片
当时间片用完时,即使进程尚未完成
也会被强制暂停并放到就绪队列末尾等待下一次调度
②抢占式调度
系统会让优先级高的线程优先使用CPU(提高抢占到的概率)
如果优先级相同,随机选择一个线程获取CPU时间片
注意:
优先级高只是建议性,不是强制性
只是提高概率,不能确定实际结果
JVM中的线程为抢占式调度,可以调节优先级
四、线程实现方式
1、继承 Thread 类
java.lang.Thread
有两种实现办法:
- 一种是新建一个类
步骤如下:
- 定义 Thread 的子类,重写 run() 方法,run()中的代码就是线程要执行的任务
- 创建 Thread 子类对象,该对象就代表一个要独立运行的线程
- 调用线程对象的 start() 方法来启动该线程
- 一种是匿名内部类
步骤如下:
- 利用 Thread 的构造函数创建对象
- 调用该对象的 start() 方法来启动线程
这里就只展示匿名内部类的写法了(无参构造):
java
public class Test_Thread {
public static void main(String[] args) {
Thread t = new Thread() {
// 重写run方法
@Override
public void run() {
System.out.println("in thread run...");
//每隔1s输出一次
for (int i = 0; i < 10; i++) {
System.out.println("thread run ");
//思考:为什么异常不能抛出?
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
}

这里有个问题
为什么异常不能抛出,而是try-catch?
原因在于:
我们是在 ++重写 run() 方法++
重写我们前面提到过,要保证与父类的一致性
父类中的 run() 并没有 throws
子类重写就不能擅自添加
Thread的多种构造函数:
1、无参构造器
javapublic Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
线程名字默认是
Thread-0
,Thread-1
, ...(自动生成)2、给线程自定义名字:
javapublic Thread(String name) { init(null, null, name, 0); }
3、把Runnable的对象当参数:
(Runnable的对象可能是实现类的、匿名内部类的等等,本身是接口不能实例化)
javapublic Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); }
4、带线程组和Runnable的:
(线程组后面会提到)
javapublic Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); }
5、线程组和自定义名称:
javapublic Thread(ThreadGroup group, String name) { init(group, null, name, 0); }
6、Runnable 和 自定义名称:
javapublic Thread(Runnable target, String name) { init(null, target, name, 0); }
7、线程组、Runnable、自定义名称:
javapublic Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); }
8、最完整的(包含栈大小的):
javapublic Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }
2、实现 Runnable 接口
java.lang.Runnable
该接口中只有一个抽象方法 run:
java
//JavaAPI-Runnable接口源码
public interface Runnable {
public abstract void run();
}
其实 Thread 类也是 Runnable 接口的实现类:
java
//JavaAPI-Thread类源码分析
public class Thread implements Runnable {
/* What will be run. */
private Runnable target;
public Thread() {
//...
}
public Thread(Runnable target) {
this.target = target;
//..
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
同样是两种实现:
- 一种是在子类中实现 Runnable 接口
- 另一种是匿名内部类
java
//1.创建Runnable实现类
class MyRunnable implements Runnable {
//2.重写run方法
@Override
public void run() {
String name = Thread.currentThread().getName();
for(int i = 20; i <= 70; i++)
System.out.println("in thread: " + name + " i: " + i);
}
}
public class Test08_Runnable {
public static void main(String[] args) {
//3.实例化对象
Runnable r = new MyRunnable();
//4.创建Thread对象
Thread th = new Thread(r);
th.setName("child-thread1");
//5.启动线程
th.start();
//匿名内部类方式 获取Runnable实现类对象
Runnable r2 = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
for(int i = 80; i >= 30; i--){
System.out.println("in thread: " + name + " i: " + i);
}
}
};
Thread th2 = new Thread(r2,"子线程2");
th2.start();
}
}
两种线程实现方式对比:
- 继承 Thread 类
好处:编程简单,可以直接使用 Thread 中的方法
坏处:可扩展性差,浪费掉唯一的继承机会(java单继承)
- 实现 Runnable 接口
好处:扩展性强,实现该接口同时还可以继承其他类
坏处:编程相对复杂,不能直接使用 Thread 类中的方法
3、实现 Callable 接口
使用不多,了解即可,较为繁琐
相关方法:
V call()
计算结果,如果无法计算,则抛出一个异常
FutureTask(Callable callable)
创建一个 FutureTask
一旦运行就执行给定的 Callable
V get()
如有必要
等待计算完成,获取其结果
实现步骤:
- 定义一个子类实现 Callable 接口
- 在子类中重写 call() 方法
- 创建子类对象
- 创建 Future 的实现类的 FutureTask 对象,将子类对象作为参数传进去
- 创建 Thread 对象,把 FutureTask 对象作为构造方法的参数
- 启动线程
- 调用 get方法,就可以获取线程结束之后的结果(可选)
java
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
//1.创建Callable的实现类
class MyCallable implements Callable<String> {
//2.重写call方法
@Override
public String call() throws Exception {
for (int i = 0; i < 100; i++) {
System.out.println("跟女孩表白" + i);
}
//返回值就表示线程运行完毕之后的结果
return "答应";
}
}
public class Test20_Callable {
public static void main(String[] args) throws Exception
{
//3.实例化Callable的实现类类对
MyCallable mc = new MyCallable();
//Thread t1 = new Thread(mc);
//4.创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
FutureTask<String> ft = new FutureTask<>(mc);
//5.创建Thread对象,并传递ft对象作为构造器参数
Thread t1 = new Thread(ft);
//String s = ft.get();
//6.开启线程
t1.start();
//7.获取线程方法执行后返回的结果
String s = ft.get();
System.out.println(s);
}
}
五、线程的核心方法
1、start()
调用者:Thread 对象
参数:无
返回值:void
作用:
启动线程,使其进入就绪状态
JVM会自动调用 run()方法
注意事项:
每个线程只能调用一次 start()
重复调用会抛出异常IllegalThreadStateException
start()不会立即执行线程代码
只是通知 JVM 该线程可以运行了
2、run()
调用者:Thread 对象 或 Runnable 实现类
参数:无
返回值:void
作用:
定义线程执行的具体逻辑
注意事项:
直接调用 run() 不会启动新线程,而是在当前线程中执行
应该通过 start() 启动
start() 和 run() 区别:start():
需要手动调用的,用于启动线程
会创建新线程,JVM负责自动调用该线程的 run()
线程进入就绪状态
run():
需要手动重写 的,是线程要执行的任务
手动调用的话就只是调用普通方法
和调用 sayHello() 一样没区别
不会创建新线程
这样就背离了初衷
被JVM调用
3、isAlive()
调用者:Thread 对象
参数:无
返回值:boolean(true表示线程仍在运行,false表示线程已经结束)
作用:
检查线程是否处于活动状态
注意事项:
线程在新建状态和终止状态返回false
线程在就绪、运行和阻塞状态返回true
(这里涉及到的线程状态后面会提到)
4、sleep(long millis)
调用者:Thread 类(静态方法)
参数:millis(休眠的毫秒数)
返回值:void
作用:
使当前正在执行的线程暂停执行指定的毫秒数
注意事项:
是静态方法,作用于当前线程
使用方式:Thread.sleep(毫秒数);
不保证精确的休眠时间,取决于系统定时器和调度器的精度
可能会抛出 InterruptedException,所以需要异常处理
java
public class SleepExample {
public static void main(String[] args) {
System.out.println("开始休眠: " + System.currentTimeMillis());
try {
Thread.sleep(2000); // 当前线程休眠2秒
} catch (InterruptedException e) {
System.out.println("休眠被中断");
Thread.currentThread().interrupt(); // 恢复中断状态
}
System.out.println("休眠结束: " + System.currentTimeMillis());
}
}
5、join()
调用者:Thread 对象
参数:
**①无参版本:**无限等待,直到调用者运行完毕
②join(long millis):等待指定毫秒数
③join(long millis, int nanos):等待指定毫秒数和纳秒数
返回值:void
作用:
在当前线程中使用,当前线程停止运行
等待 调用join的线程 运行完毕或运行指定毫秒数后再开始运行
注意事项:
可能抛出异常
InterruptedException
,需要异常处理等待期间若其他线程中断了当前线程,会抛出异常
java
public class Test {
public static void main(String[] args) {
Thread t2 = new Thread(new Runnable(){
@Override
public void run(){
try {
Thread.sleep(200); //等待t1执行到i = 3
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");
}
}
},"t2");
Thread t1 = new Thread(new Runnable(){
@Override
public void run(){
for (int i = 1; i <= 5; i++) {
if(i == 3){
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "正在运行第" + i + "次");
}
}
},"t1");
t1.start();
t2.start();
}
}
为了更好测试,刚开始不让 t1 和 t2 抢夺时间片,先让 t2 小睡一会

6、interrupt()
调用者:Thread 对象
参数:无
返回值:void
作用:
中断线程
注意事项:
不会真正停止线程,只是设置中断状态
如果线程处于阻塞状态(如 sleep、wait)
会抛出异常 InterruptedException
java
public class InterruptExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程正在运行...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("线程被中断");
// 注意:InterruptedException会清除中断状态
Thread.currentThread().interrupt(); // 重新设置中断状态
break;
}
}
System.out.println("线程结束");
});
thread.start();
try {
Thread.sleep(3000); // 主线程等待3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 中断子线程
}
}
7、currentThread()
调用者:Thread 类(静态方法)
参数:无
返回值:当前正在执行的线程对象
作用:
返回对当前正在执行的线程对象的引用
注意事项:
常用于获取当前线程的信息或者设置线程属性
以及使用 Thread 的静态方法
java
public class CurrentThreadExample {
public static void main(String[] args) {
Thread current = Thread.currentThread();
System.out.println("当前线程名称: " + current.getName());
System.out.println("当前线程ID: " + current.getId());
System.out.println("当前线程优先级: " + current.getPriority());
// 设置线程名称
current.setName("主线程");
System.out.println("修改后线程名称: " + current.getName());
}
}
六、线程状态
- 新建状态(New):线程对象被创建,但还没有调用 start() 方法
- 就绪状态(Runnable):调用了 start() 方法,等待 CPU 调度
- 终止状态(Terminated):线程执行完毕 或异常退出
- 阻塞状态(Blocked):由于某种原因放弃 CPU 使用权
- 等待状态(Waiting):线程在没有指定时间的情况下进入等待,必须等其他线程显示唤醒才能继续执行
- 超时等待(Timed Waiting):线程在指定时间内进入等待,时间到达后会自动苏醒,不需要其他线程显示唤醒

java
public class ThreadStateExample {
public static void main(String[] args) throws InterruptedException {
// NEW 新建状态
Thread newThread = new Thread(() -> {
System.out.println("线程正在运行");
});
System.out.println("NEW状态: " + newThread.getState()); // NEW
// 启动线程进入 Runnable 可运行状态
newThread.start();
System.out.println("RUNNABLE状态: " + newThread.getState()); // RUNNABLE
// Blocked 锁阻塞状态示例
Object lock = new Object();
Thread blockedThread = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(5000); // 持有锁5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread blockedThread2 = new Thread(() -> {
synchronized (lock) { // 尝试获取已被占用的锁
System.out.println("获取到锁");
}
});
blockedThread.start();
Thread.sleep(100); // 确保第一个线程先获取锁
blockedThread2.start();
Thread.sleep(100); // 等待第二个线程尝试获取锁
System.out.println("BLOCKED状态: " + blockedThread2.getState()); // BLOCKED
// Waiting 等待状态示例
Object waitLock = new Object();
Thread waitingThread = new Thread(() -> {
synchronized (waitLock) {
try {
waitLock.wait(); // 进入等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
waitingThread.start();
Thread.sleep(100);
System.out.println("WAITING状态: " + waitingThread.getState()); // WAITING
// Timed Waiting 超时等待状态示例
Thread timedWaitingThread = new Thread(() -> {
try {
Thread.sleep(5000); // 进入超时等待状态
} catch (InterruptedException e) {
e.printStackTrace();
}
});
timedWaitingThread.start();
Thread.sleep(100);
System.out.println("TIMED_WAITING状态: " + timedWaitingThread.getState()); // TIMED_WAITING
// TERMINATED 终止状态示例
Thread terminatedThread = new Thread(() -> {
System.out.println("线程任务完成");
});
terminatedThread.start();
terminatedThread.join(); // 等待线程执行完毕
System.out.println("TERMINATED状态: " + terminatedThread.getState()); // TERMINATED
// 唤醒等待线程
synchronized (waitLock) {
waitLock.notify();
}
}
}