【Java笔记】多线程的简单归纳
一、名词概念
1.1 串行和并发
串行: 多个任务依次按顺序执行。
并发: 多个任务同时执行。
1.2 进程和线程
进程:
简单来说就是对 一个程序运行所占用的各种资源 的描述。
进程之间资源 不共享 。
线程:
一个进程中任务执行的最小单元,一个进程中 至少有一个 线程。
线程之间资源 可以共享 ,被多个线程共享的资源叫 临界资源。
二、线程的生命周期
一个线程从实例化完成到销毁的过程,叫做线程的 生命周期 。
一个线程的生命周期拥有 新生,就绪,运行,阻塞 ,死亡 这几个状态。
状态 | 解释 |
---|---|
新生态 | 线程对象被实例化,单还没有做任何操作 |
就绪态 | 线程被开启,开始争夺CPU资源 |
运行态 | 线程抢到CPU时间片,开始执行该线程的逻辑 |
阻塞态 | 一个线程在运行过程中,由于某些操作,放弃CPU时间片, 并且不再参与CPU资源竞争,此时线程处于挂起状态 |
死亡态 | 一个线程对象被销毁 |
三、线程的创建
3.1 线程的创建方式
1. 继承Thread类
2.实现Runnable接口
3.2 start()和run()方法
相同:
都能执行线程内的逻辑。
不同:
start()方法会让线程进入就绪状态,而run()不会。
调用start()方法,逻辑将在子线程执行,run()则会在当前线程执行。
四、线程的常用方法
4.1 线程的命名
1.通过setName()方法
2.通过Thread类的构造方法
4.2 线程的休眠
使用 sleep() 方法,休眠时间单位为 毫秒,使线程由 运行状态 进入 阻塞状态。
休眠时间结束后重新进入就绪状态。
4.3 线程的优先级
对线程抢到CPU时间片的 概率 的描述。
通过 setPriority() 方法,设置线程的优先级。参数取值为[0,10]的 整数 。
优先级越高,抢到CPU时间片的概率就越高;优先级越,低抢到的CPU时间片的概率就越低。
4.4 线程的礼让
使用 yield() 方法,使当前的线程释放占用的CPU资源,进入 就绪状态 。
五、临界资源问题
定义:
在并发编程中对临界资源的处理不当, 导致数据不一致的问题。
原因:
一个线程将临界资源置于中间状态后, 另一个线程访问了这个中间状态并基于此中间状态做了进一步的处理。
解决办法:
使用 线程锁 来防止多个线程访问同一个临界资源。
六、锁
6.1 同步锁(synchronize)
同步代码块:
运行状态的线程进入 synchronize 同步代码块会为临界资源上锁,而其他线程在碰到锁标记时会进入 锁池 。
锁池内的线程争抢锁标记以进入就绪状态,临界资源解锁后,抢到锁标记的线程将进入同步代码块继续运行。
synchronize的括号中可以是 对象 也可以是 类 ,但是要确保每一个线程看到的是同一把锁,以下这种写法是不起作用的:
同步方法:
使用 synchronize 关键字修饰方法。
- 如果修饰的是静态方法,那么该锁为 当前类 的类锁;
- 如果修饰的是非静态方法,则为 this 对象锁。
6.2 显示锁
使用 ReentrantLock 创建锁对象。 lock() 和 unlock() 分别用于上锁和解锁临界资源。
6.3 死锁
定义: 多个线程持有对方所需要的锁对象,而又不释放自己的锁。
死锁条件:
① 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某 资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待;
② 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源 已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放;
③ 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放);
④ 循环等待条件:若干进程间形成首尾相接循环等待资源的关系。
这四个条件是死锁的必要条件,
只要系统发生死锁,这些条件必然成立;
而只要上述条件之一不满足,就不会发生死锁。
解决方法:
wait() 方法:是Object类中的一个方法,使当前线程释放自己的锁标记,并让出CPU资源,进入等待队列。
notify() 方法:是Object类中的一个方法,唤醒等待队列中的一个等待对应锁标记的线程,使得这个线程进入锁池。
notifyAll() 方法:是Object类中的一个方法,唤醒等待队列的等待对应锁标记的所有线程,使它们进入锁池。