目录
一、线程常用方法
1.1启动一个线程-start()
1.2中断一个线程
1.2.1使用自定义的变量来作为标志位.
1.2.2使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.
1.2.3观察标志位是否清除
1.3等待一个线程-join()
1.4获取当前线程引用
1.5休眠当前线程
二、线程的状态
2.1观察线程的所有状态
2.2线程状态和状态转移的意义
2.3观察线程的状态和转移
2.3.1关注 NEW 、RUNNABLE 、TERMINATED 状态的转换
2.3.2关注 WAITING 、BLOCKED 、TIMED_WAITING 状态的转换
一、线程常用方法
1.1启动一个线程-start()
之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
- 覆写 run 方法是提供给线程要做的事情的指令清单
- 线程对象可以认为是把 李四、王五叫过来了
- 而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。
调用 start 方法, 才真的在操作系统的底层创建出一个线程.
1.2中断一个线程
李四一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。
目前常见的有以下两种方式:
1. 通过共享的标记来进行沟通
2. 调用 interrupt() 方法来通知
1.2.1使用自定义的变量来作为标志位.
public class ThreadDemo9 {
//布尔类型成员变量:Lambda表达式进行变量捕获
//自定义标志位
public static boolean isQuit = false;
public static void main(String[] args) {
Thread thread =new Thread(()->{
while(!isQuit){
System.out.println("Hello Thread");
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
}
}
System.out.println("Thread线程终止");
});
thread.start();
//在主线程中修改isQuit
try{
Thread.sleep(3000);
}
catch(InterruptedException e){
e.printStackTrace();
}
isQuit=true;
}
}
如果将isQuit从成员变量改为局部变量,代码能否正常运行?
不能正常运行。因为变量捕获
1.2.2使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.
Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记
public class ThreadDemo10 {
public static void main(String[] args) {
Thread thread=new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
//currentThread是获取到当前线程的实例
//此处currentThread得到的对象是Thread
//isInterrupted是Thread自带的标志位
System.out.println("Hello Thread");
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
e.printStackTrace();
//手动让其结束
break;
}
}
});
thread.start();
try{
Thread.sleep(3000);
}
catch(InterruptedException e){
e.printStackTrace();
}
//把Thread内部的标志位设置为true
thread.interrupt();
}
}
thread 收到通知的方式有两种:
1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程.
2. 否则,只是内部的一个中断标志被设置,thread 可以通过Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
1.2.3观察标志位是否清除
标志位是否清除, 就类似于一个开关.
Thread.isInterrupted() 相当于按下开关, 开关自动弹起来了. 这个称为 "清除标志位"
Thread.currentThread().isInterrupted() 相当于按下开关之后, 开关弹不起来, 这个称为
"不清除标志位".
使用 Thread.isInterrupted() , 线程中断会清除标志位.
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.interrupted());
}
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
thread.start();
thread.interrupt();
}
}
true // 只有一开始是 true,后边都是 false,因为标志位被清
false
false
false
false
false
false
false
false
false
使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
thread.start();
thread.interrupt();
}
}
true // 全部是 true,因为标志位没有被清
true
true
true
true
true
true
true
true
true
1.3等待一个线程-join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。
public class ThreadDemo11 {
public static void main(String[] args) {
Thread thread =new Thread(()->{
System.out.println("Hello Thread");
});
thread.start();
try{
thread.join();
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Hello Main");
}
}
1.4获取当前线程引用
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
1.5休眠当前线程
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}
二、线程的状态
2.1观察线程的所有状态
线程的状态是一个枚举类型 Thread.State
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
public class ThreadDemo12 {
public static void main(String[] args) throws InterruptedException {
Thread thread =new Thread(()->{
while(true){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
});
System.out.println(thread.getState());
thread.start();
Thread.sleep(2000);
System.out.println(thread.getState());
}
}
- NEW: 安排了工作, 还未开始行动
- RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.
- BLOCKED: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- TIMED_WAITING: 这几个都表示排队等着其他事情
- TERMINATED: 工作完成了.
2.2线程状态和状态转移的意义
还是我们之前的例子:
刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;
当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行工作人员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度;当李四、王五因为一些事情需要去忙,例如需要填写信息、回家取证件、发呆一会等等时,进入BLOCKED 、WATING 、TIMED_WAITING 状态,至于这些状态的细分,我们以后再详解;如果李四、王五已经忙完,为 TERMINATED 状态。所以,之前我们学过的 isAlive() 方法,可以认为是处于不是 NEW 和 TERMINATED 的状态都是活着的。
2.3观察线程的状态和转移
2.3.1关注 NEW 、RUNNABLE 、TERMINATED 状态的转换
public class ThreadStateTransfer {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 1000_0000; i++) {
}
}, "李四");
System.out.println(t.getName() + ": " + t.getState());;
t.start();
while (t.isAlive()) {
System.out.println(t.getName() + ": " + t.getState());;
}
System.out.println(t.getName() + ": " + t.getState());;
}
}
2.3.2关注 WAITING 、BLOCKED 、TIMED_WAITING 状态的转换
public static void main(String[] args) {
final Object object = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();}
}
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
System.out.println("hehe");
}
}
}, "t2");
t2.start();
}
使用 jconsole 可以看到 t1 的状态是 TIMED_WAITING , t2 的状态是 BLOCKED
修改上面的代码, 把 t1 中的 sleep 换成 wait
public static void main(String[] args) {
final Object object = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
try {
// [修改这里就可以了!!!!!]
// Thread.sleep(1000);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1");
...
}
使用 jconsole 可以看到 t1 的状态是 WAITING
- BLOCKED 表示等待获取锁, WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.
- TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒