1.概念
- 一个程序可以包括多个子任务,可串/并行
- 每个子任务可以称为一个线程
- 如果一个子任务阻塞,程序可以将CPU调度另外一个子任务进行工作。这样CPU还是保留在本程序中,而不是被调度到别的程序(进程)去。这样,提高本程序所获得CPU时间和利用率
2.多线程实现
多线程创建
- java.lang.Thread
- 线程继承Thread类,实现run方法
1
2
3
4
5public class Thread1 extends Thread {
public void run() {
System.out.println("run");
}
} - java.lang.Runnable接口
- 线程实现Runnable接口,实现run方法
1
2
3
4
5public class Thread2 implements Runnable {
public void run() {
System.out.println("run");
}
}
多线程启动
- start方法,会自动以新进程调用run方法
1 | //继承Thread类 |
- 直接调用run方法,将变成串行执行
- 同一个线程,多次start会报错,只执行第一次start方法
- 多个线程启动,其启动的先后顺序是随机的
- 线程无需关闭,只要其run方法执行结束后,自动关闭
- main函数(线程)可能早于新线程结束,整个程序并不终止
- 整个程序终止是等所有的线程都终止(包括main函数线程)
实现对比
- Thread vs Runnable
- Thread占据了父类的名额,不如Runnable方便
- Thread 类实现Runnable
- Runnable启动时需要Thread类的支持
- Runnable 更容易实现多线程中资源共享
- 结论:建议实现Runnable接口来完成多线程
3.多线程信息共享
- 通过共享变量在多个线程中共享消息
- 如果继承了Thread类,只能使用static变量
- 如果实现了Runnable接口,可以使用同一个成员变量
信息共享问题
- 工作缓存副本
- 解决方法
- 采用volatile关键字修饰变量
1
private volatile boolean flag = false;
- 保证不同线程对共享变量操作时的可见性
- 解决方法
- 关键步骤缺乏加锁限制
- 解决方法:关键步骤加锁限制
- 互斥:某一个线程运行一个代码段(关键区),其他线程不能同时运行这个代码段
- 同步:多个线程的运行,必须按照某一种规定的先后顺序来运行
- 互斥是同步的一种特例
- 互斥的关键字是synchronized
- synchronized代码块/函数,只能一个线程进入
- synchronized加大性能负担,但是使用简便
1
2
3
4
5
6
7
8
9public synchronized void sale() {
//TODO
}
//等同于
public void sale() {
synchronized(this) {
//TODO
}
}
- 解决方法:关键步骤加锁限制
4.多线程管理
线程状态
NEW 刚创建(new)
RUNNABLE 就绪态(start)
RUNNING 运行中(run)
BLOCK 阻塞(sleep)
TERMINATED 结束
Thread的部分API已经废弃
- 暂停和恢复 suspend/resume
- 消亡 stop/destroy
线程阻塞/和唤醒
- sleep,时间一到,自己会醒来
- wait/notify/notifyAll,等待,需要别人来唤醒
- join,等待另外一个线程结束
- interrupt,向另外一个线程发送中断信号,该线程收到信号,会触发InterruptedException(可解除阻塞),并进行下一步处理
线程被动地暂停和终止
- 依靠别的线程来拯救自己
- 没有及时释放资源
线程主动暂停和终止
- 定期监测共享变量
- 如果需要暂停或者终止,先释放资源,再主动动作
- 暂停:Thread.sleep(),休眠
- 终止:run方法结束,线程终止
多线程死锁
- 每个线程互相持有别人需要的锁
- 预防死锁,对资源进行等级排序
守护(后台)线程
- 普通线程的结束,是run方法运行结束
- 守护线程的结束,是run方法运行结束,或main函数结束
- 守护线程永远不要访问资源,如文件或数据库等
1
2
3Thread1 t = new Thread1();
t.setDaemon(true);
t.start();线程查看工具 jvisualvm
线程组管理
- 线程组ThreadGroup
- 线程的集合
- 树形结构,大线程组可以包括小线程组
- 可以通过enumerate方法遍历组内的线程,执行操作
- 能够有效管理多个线程,但是管理效率低
- 任务分配和执行过程高度耦合
- 重复创建线程、关闭线程操作,无法重用线程
1 | // 创建线程组 |