一、线程概念
进程
进程是应用程序的一次执行,程序一旦执行就会产生进程,比如在运行java应用程序后,会产生一个JVM进程。
线程
进程会产生若干个线程,线程则包含于进程之中,是相对进程更小的运行单位,线程之间可以共享进程的资源,线程之间的资源则是相互独立。比如JVM进程拥有堆和方法区,线程拥有虚拟机栈、程序计数器、本地方法栈,线程可以共享进程的堆和方法区。
二、线程状态
- Object.wait:让当前线程暂停(可指定时间),并释放对象的monitor,wait之前必须先获取到对象的monitor。直到由其他线程调用notify或notifyAll,或者wait的时间到期。notify会唤醒申请Object monitor的一个线程,随机选择;notifyAll会唤醒申请Object monitor的所有线程。native本地方法。
- Thread.sleep:让当前线程暂停指定时间,线程会让出cpu,但不会释放对象的monitor。native本地方法。
- Thread.join:阻塞当前线程,直到join线程运行完成为止。主线程中执行t.join后,主线程会等待t线程任务执行完成后,再继续执行。
join通过wait/notifyAll来实现,join方法执行时,相当于在主线程中执行了t.wait(),当t线程执行完成,退出之前,jvm会触发t.notifyAll方法,唤醒主线程。
public class Thread implements Runnable {
...
public final void join() throws InterruptedException {
join(0);
}
...
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) { //判断是否携带阻塞的超时时间,等于0表示没有设置超时时间
while (isAlive()) {//isAlive获取线程状态,无线等待直到previousThread线程结束
wait(0); //调用Object中的wait方法实现线程的阻塞
}
} else { //阻塞直到超时
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
...
- Thread.yield:让当前线程暂停,让出cpu控制权,释放对象锁,进入就绪队列;cpu只会选取优先级不小于它的线程。native本地方法。
- Thread.interrupted:允许一个线程去设置另一个线程的中断状态,进而改变另一个线程的运行逻辑。
//主线程中开启新线程t1,t1自旋判断中断状态执行任务
Thread t1 = new Thread(new Runnable(){
while(!Thread.interrupted()){
//执行任务
}
}
);
t1.start();
//主线程中中断t1线程
t1.interrupt();
三、线程死锁
死锁概念
程序中有多个线程在等待资源的释放,而处于无限等待状态,导致程序无法正常运行下去,举个例子,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
死锁产生的充分必要条件是,
- 互斥条件:该资源任意一个时刻只由一个线程占用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。