1. 基本概念
1.1 程序
用某种语言编写的一组指令的集合,一段静态的代码
1.2 进程
程序的一次执行过程,或正在运行的一个程序,是一个动态的过程
- 进程是系统进行资源分配的基本单位
1.3 线程
进程可以进一步细化为线程,是程序内部的一条执行路径
- 若一个进程可同一时间并行执行多个程序,就是支持多线程的
- 线程是CPU调度和执行的最小单位,每个线程拥有独立的运行栈和程序计数器
- 线程切换的开销小
- 一个进程中的多个线程可以共享相同的内存单元(堆和方法区),使进程间的通信变得便捷,但也可能带来安全隐患
1.4 多线程的优点
- 提高计算机CPU的利用率
- 改善程序结构,将长而复杂的进程分为多个线程,独立运行,利于理解和维护
- 增强用户体验,对图形化界面更有意义
2. 线程的创建和使用
2.1 创建方式一:继承Thread类
- 继承Thread类
- 重写
run()方法
-
创建该子类对象,调用
start()方法- 启动当前线程
- 调用当前线程的
run()方法
使用匿名对象创建:
new Thread (){
@Override
public void run () {
super.run () ;
}
} .start () ;
复制代码
2.2 创建方式二:实现Runnable接口
- 实现
Runnable接口
- 实现
run()方法
- 创建实现类的对象
- 将此对象作为参数传递到
Thread类的构造器中(赋值到Runnable类型的target),创建Thread类的对象
- 通过
Thread类的对象调用start()方法(调用target的run()方法)
2.3 线程常用方法
start():启动当前线程,调用当前线程的run()方法
run():创建的线程需要重写的方法,将需实现的逻辑声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字
yield():释放当前CPU的执行权,重新竞争执行权
join():当前线程进入阻塞状态,使调用该方法的线程先执行完毕,再结束当前线程的阻塞
stop():强制结束线程
sleep():使线程休眠,单位为毫秒
isAlive():判断线程是否存活
2.4 线程的调度
-
调度策略
- 时间片
- 抢占式:高优先级的线程抢占CPU
-
Java的调度方法
- 同优先级的线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级的线程使用抢占式策略
-
线程的优先级
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5
-
相关方法
getPriority():获取线程优先级setPriority():设置线程优先级
-
说明
- 线程创建时继承父线程的优先级
- 低优先级是获得调度的概率低,并非一定在高优先级线程后被调用
2.5 两种方式对比
-
开发中优先选择实现
Runnable接口的方式- 没有类的单继承性的局限性
- 适合处理多个线程有共享数据的情况
Thread类也实现了Runnable接口
- 两种方式都需要覆盖
run()方法
3. 线程的生命周期
JDK中用Thread.State类定义了线程的几种状态:
在一个线程完整的生命周期中通常要经历如下的五种状态:
- 新建:线程被声明或创建
- 就绪:新建的线程被
start()后,进入线程队列等待CPU时间片,此时具备了运行的条件,但是并未分配到CPU资源
- 运行:就绪的线程被调度并获得了CPU资源
- 阻塞:被人为挂起或执行输入输出操作,让出了CPU并临时中止自己的执行
- 死亡:线程完成了它的全部工作或被提前强制性地中止或出现异常导致结束
4. 线程的同步机制
4.1 同步代码块
synchronized ( 同步监视器 ) {
//需要被同步的代码(操作共享数据的代码)
}
复制代码
- 同步监视器:俗称锁,任何一个类的对象都可以充当锁;多个线程必须共用同一把锁。
4.2 同步方法
- 操作共享数据的代码恰好是一个方法,则将这个方法声明为同步的
-
也需注意多个线程共用同一把锁,只是锁不需要显式声明
- 非静态方法中锁是:this
- 静态方法中锁是:当前类本身
4.3 Lock锁
- 实例化
ReentrantLock
- 调用锁定方法:
lock()
- 调用解锁方法:
unlock()
public class LockTest implements Runnable () {
ReentrantLock lock = new ReentrantLock ();//实例化ReentrantLock
public void run(){
try{
lock.lock();//调用锁定方法
...
//对共享数据的操作代码
}finally{
lock.unlock(); //调用解锁方法
}
}
}
复制代码
- 与
synchronized的区别:手动启动同步,手动结束同步
5. 线程的死锁
- 概念:不同的线程分别占用对方需要的同步锁不放弃,都在等待对方放弃自己需要的同步锁;此时所有的线程都处于阻塞状态,无法继续
-
解决:
- 减少同步资源的定义
- 尽量避免嵌套同步