问:线程有哪些状态?
- 新建(new):创建后尚未启动的线程的状态(新创建的线程但还没有调用start方法)
- 运行(Runnable):包含Running和Ready(Running线程位于可运行线程中,等待调度选中获取CPU使用权,处于Ready状态的线程位于线程池中,等待调度选中,选中获取CPU时间后变为Running状态)
- 无限期等待(Waiting):不会被分配CPU执行时间,需要显式唤醒(notify、notifyAll),以下3个方法会让线程陷入无限期等待状态:
- 没有设置Timeout参数的Object.wait()方法
- 没有设置Timeout参数的Thread.join()方法
- LockSupport.park()方法
- 限期等待(Timed Waiting):在一定时间后会由系统自动唤醒(也不会分配CPU执行时间)以下方法会让线程进入限期等待:
- Thread.sleep()方法
- 设置了Timeout参数的Object.wait()方法
- 设置了Timeout参数的Thread.join()方法
- LockSupport.parkNanos()方法
- LockSupport.parkUntil()方法
- 阻塞(Blocked):等待获取排他锁(如synchronized同步代码块或方法会出现此状态)
- 结束(Terminated):已终止线程的状态,线程已经结束执行(结束状态的线程再调用start方法会抛出java.lang.IllegalThreadStateException异常)
问:sleep和wait的区别?
- sleep是Thread类的方法,wait是Object类的方法
- sleep方法是可以在任何方法使用
- wait方法只能在synchronized方法或者synchronized块中使用(因为只有获取锁了才能释放锁)
最主要的本质区别:
- Thread.sleep只会让出CPU,不会导致锁行为的改变
- Object.wait不仅让出CPU,还会释放已经占有的同步资源锁
Thread.java中的
Object.java中的
package interview.thread;
/**
* wait和sleep的区别
* @Author: cctv
* @Date: 2019/5/17 11:49
*/
public class WaitSleepDemo {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(() -> {
System.out.println("thread A is waiting to get lock");
synchronized (lock) {
try {
System.out.println("thread A get lock");
// 先等B运行起来再到下一步
Thread.sleep(20);
System.out.println("thread A do wait method");
// wait让出CPU和锁 B会抢到锁执行B后再执行A
// lock.wait(1000);
// sleep不会让出锁 A执行完了之后再执行B
Thread.sleep(1000);
System.out.println("thread A is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// 先启动A拿到锁,再让B也运行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
System.out.println("thread B is waiting to get lock");
synchronized (lock) {
try {
System.out.println("thread B get lock");
System.out.println("thread B do wait method");
Thread.sleep(10);
System.out.println("thread B is done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
sleep结果:
wait结果:
问:notify和notifyAll的区别?
首先了解2个概念:
- 锁池EntryList(一个锁资源被占用之后其他其他线程想要获取锁就进入了阻塞状态,在一个地方等待锁的释放,这个地方就是锁池)
- 等待池WaitSet(线程调用某对象的wait方法之后旧进入等待状态,进入等待池中,进入等待吃的线程不会去竞争该对象的锁)
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
notify是随机选择一个处于等待池的线程进入锁池去竞争获取锁的机会
问:yiled的含义?
当调用Thread.yield()函数时,会给线程调度器一个当前线程愿意让出CPU使用的暗示,但是线程调度器可能会忽略这个暗示。
yield并不会让出当前占用的锁
问:如何中断线程?
已被抛弃的方法:
- 通过调用stop()方法停止线程(这种方法太过暴力,而且是不安全的。突然调用stop停止了线程,导致线程的一些清理工作无法完成,而且会释放锁,可能导致数据不同步的问题)
- 通过调用suspend()和resume()方法(原因和stop类似)
目前使用的方法:
- 调用interrupt(),通知线程应该中断了
- 如果线程处于被阻塞状态,那么线程将立刻退出被阻塞状态,并抛出一个InterruptedException异常
- 如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常运行,不受影响
- 需要被调用的线程配合中断:
- 在阻塞阻塞线程里,处理InterruptedException异常,并在catch里做好相关清理工作
- 在正常运行任务时,经常检查本线程的中断标志位(Thread.currentThread().isInterrupted()方法判断),如果被设置了中断标志就自行停止线程
测试:
package interview.thread;
/**
* 中断线程demo
*
* @Author: cctv
* @Date: 2019/5/20 10:26
*/
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Runnable interruptTask = new Runnable() {
@Override
public void run() {
int i = 0;
Thread t = Thread.currentThread();
// 加上注释看阻塞状态的处理 去掉注释看工作状态的处理
// try {
while (!t.isInterrupted()) {
// Thread.sleep(100);
i++;
System.out.println(t.getName() + "(" + t.getState() + ") loop " + i);
}
System.out.println(t.getName() + "(" + t.getState() + ") Thread isInterrupted is true. loop is" + i);
// } catch (InterruptedException e) {
// System.out.println(t.getName() + "(" + t.getState() + ") catch InterruptedException. loop is " + i);
// }
}
};
Thread t1 = new Thread(interruptTask, "t1");
System.out.println(t1.getName() + "(" + t1.getState() + ") is new");
t1.start();
System.out.println(t1.getName() + "(" + t1.getState() + ") is started");
Thread.sleep(300);
t1.interrupt();
System.out.println(t1.getName() + "(" + t1.getState() + ") is interrupted");
Thread.sleep(300);
System.out.println(t1.getName() + "(" + t1.getState() + ") is terminated");
}
}