以下全部笔记来源于葛一鸣老师的并发编程讲解。
概念
并发级别
- 阻塞
- 无障碍
- 无锁
- 无等待
其中无障碍、无锁、无等待
都是非阻塞式的。
无障碍(Obstruction-Free)
(1) 无障碍是一种最弱的非阻塞调度
(2)自由出入临界区,宽进严出
(3)无竞争时,有限步内完成操作
(4)有竞争时,回滚数据
举例:一群线程同时进入临界区,当某线程发现数据被修改时,回滚自己的操作,重新进行操作。
注:有可能所有线程互相影响,都不能完成任务。
无锁(Lock-Free)
(1)是无障碍的
(2)保证有一个线程可以胜出
无等待(Wait-Free)
(1)无锁的
(2)要求所有线程都必须在有限步内完成
(3)无饥饿的
线程的相关操作
start方法
在新线程中运行run方法。
常见错误:直接使用run方法,在当前线程运行run方法,相当于普通的类方法(自己以前犯过)。
stop方法
暴力停止线程。
interrupt方法
给线程设置一个中断标记。
isInterrupted()判断是否被设置中断标记。
Thread.interripted()静态方法,判断是否被中断,并清除当前的中断标记。
sleep方法
将当前线程休眠N毫秒,可能会有InterruptedException
异常。通常是在休眠时,被设置了中断状态,会立马被Catch到,且清除中断标记位(通常根据需要,重新设置中断标志)。
suspend方法/resume方法
suspend方法会将Thread挂起,不释放资源。
resume方法会取消挂起。
通常配对使用,但如果不能保证resume的话,线程将占用资源不被释放。
yeild方法
静态方法,释放当前线程已拿到的CPU资源,加入当前的竞争队列(有可能再次获得资源)。
join方法
阻塞式的等待线程结束
// join的本质
while(isAlive()){
wait(0);
}
setDaemon方法
将线程设置为守护线程,需要在线程start前设置。
守护线程
在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程就可以理解为守护线程。
当一个Java应用内,只有守护线程时,java虚拟机就会自然退出。
public static void main(String[] args){
Thread t = new ThreadDaemon();
t.setDaemon(true);
t.start();
}
// 程序会立马退出
setPriority方法
设置线程的优先级,高优先级更容易抢占资源。
线程同步
关键字synchronized
- 指定加锁对象:对给定对象加锁,进入同步代码前要获得
给定对象
的锁。
public static object = new Object();
synchronized(object){
// 同步块,对object加锁
}
- 直接作用于
实例
方法:相当于对当前实例
加锁,进入同步代码前要获得当前实例
的锁。
public synchronized void setInteger(int i){
// 同步块,对当前实例对象加锁
this.i = i ;
}
- 直接作用于
静态
方法:相当于对当前类
加锁,进入同步代码前要获得当前类
的锁。
public static synchronized void setInteger(int i){
// 同步块,对Class类类型对象加锁
this.i = i ;
}
Object.wait()/Object.notify()/Object.notifyAll()
wait方法会释放当前获得锁,调用object的wait方法前,必须拿到object实例的锁,如下代码
synchronized(object){
try{
// 必须在synchronized块里面,不然会报监视器异常的错误
object.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
notify方法会唤醒在WAIT状态的object锁下的一个
线程,同样,调用object的notify方法前,必须拿到object实例的锁。
synchronized(object){
try{
// 必须在synchronized块里面,不然会报监视器异常的错误
object.notify();
}catch(InterruptedException e){
e.printStackTrace();
}
}
注:线程wait后,被notify唤醒,如果别的线程没有释放锁,那么该线程还是会等待到拿到锁为止才会继续运行。
notify方法会唤醒在WAIT状态的object锁下的所有
线程。