JavaWeb~玩转Thread类 如何中断一个线程 认识线程的状态

Thread类

  • Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
  • Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

构造方法

  • Thread() 创建线程对象
  • Thread(Runnable target) 使用Runnable创建线程对象
  • Thread(String name) 创建一个有名字的线程
  • Thread(Runn target , String name) 创建线程并命名
Thread thread = new Thread("listen")
  • 让一个线程有名字对线程的执行没有任何影响, 意义就是为了让调试方便

常见方法获取属性

  • getId() 返回ID
  • getName() 返回name
  • getState() 返回状态
  • getPriority() 返回优先级
  • isDaemon() 是否后台线程
  • isAlive() PCB是否存活
  • isInterrupted() 是否被中断
public class ThreadTest2 {
    public static void main(String[] args) {
        Thread thread = new Thread("listen") {
            @Override
            public void run() {
               //死循环让该线程一直执行
                while (true) {

                }
            }
        };
        thread.start();
        System.out.println(thread.getId());
        System.out.println(thread.getName());
        System.out.println(thread.getState());
        System.out.println(thread.getPriority());
        System.out.println(thread.isDaemon());
        System.out.println(thread.isAlive());
        System.out.println(thread.isInterrupted());
    }
}

11
listen
RUNNABLE
5
false
true
false
  1. ID 是线程的唯一标识,不同线程不会重复
  2. 名称是各种调试工具用到
  3. 状态表示线程当前所处的一个情况,下面我们会进一步说明
  4. 优先级高的线程理论上来说更容易被调度到
  5. 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
  6. 是否存活,即简单的理解,为 run 方法是否运行结束了
  7. 线程的中断问题,下面我们进一步说明

启动线程 start()

public class TheardTest1 {
    public static void main(String[] args) {
        Thread thread = new Thread("listen") {
            @Override
            public void run() {
                System.out.println("hello thread");
            }
        };
        //thread.run();
        thread.start();

    }
}

  • 定义的新线程thread与原有线程之间是并发执行,只有新线程调用start()方法后,才能真正在内核中创建线程
  • 如果是使用thread.run() 就上面代码而言执行结果是一样的,但本质不一样, 这样的方式只是一个普通的方法调用,没有创建新的线程,输出语句是在原有线程里执行的
  • 而使用start() 方法时创建一个新线程,输出语句由新线程执行

中断线程 interrupt()

目前常见的俩种中断线程的方式:

通过共享的标记来进行沟通(温和)

  • 这种方式比较温和,就是让run方法执行完,例如下面的例子当新线程的sleep执行到100ms时, 我们把isQuit 改为true 但是当前线程并不会立即停止退出,而是把剩下的900ms等待玩,才会结束线程
public class ThreadTest3 {
    private static boolean isQuit = false;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread("listen") {
            @Override
            public void run() {
                while (!isQuit) {
                    System.out.println(Thread.currentThread().getName() + "在忙着打游戏");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("赶快走!!!");
            }
        };
        thread.start();
        Thread.sleep(5000);
        System.out.println(thread.getName() + "你女朋友来电话让你逛街");
        isQuit = true;
    }
}

调用 interrupt() 方法来通知(刚烈)

此时线程会有俩种情况

  1. 当前线程在sleep 或者 wait 中, 这种情况中断就是给线程触发一个异常,线程就会收到这个异常,针对这个异常如何处理就是catch里的事情了
  2. 当前线程不在sleep或者wait 这个操作会将isInterrupted置为true 来中断线程
  • 本质上上面两个动作在一个线程里是同时工作的
  • 重点说明下第二种方法:
    通过 thread 对象调用 interrupt() 方法通知该线程停止运行, thread 收到通知的方式有两种:
  • 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,并清除中断标志
  • 否则,只是内部的一个中断标志被设置,thread 可以通过以下俩种方式退出run
  1. Thread.interrupted() 判断当前线程的中断标志被设置,并清除中断标志(中断一次将中断结果显示为true,但同时又将中断标记设回false)
  2. Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志(不清楚中断标记的意思是只要中断一次将.isInterrupted()改为了true就一直会是true)(推荐, 通知收到的更及时,即使线程正在 sleep 也可以马上收到 )
public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.interrupted());
                }
            }
        };
        thread.start();
        thread.interrupt();
        thread.join();
        System.out.println("================");
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            }
        };
        thread1.start();
        thread1.interrupt();
    }
}

true
false
false
false
false
false
false
false
false
false
================
true
true
true
true
true
true
true
true
true
true

public class ThreadTest4 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread("listen") {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "在打游戏");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        //因为在sleep中中断也会清除中断标记,所以要用break结束循环
                        break;
                    }
                }
                System.out.println("赶快去!!!");
            }
        };
        thread.start();
        Thread.sleep(5000);
        System.out.println(thread.getName() + "女朋友叫你去逛街");
        thread.interrupt();
    }
}

listen在打游戏
listen在打游戏
listen在打游戏
listen在打游戏
listen在打游戏
listen女朋友叫你去逛街
赶快去!!!
  • 注意一点 如sleeo(5000) 不会真正达到5000ms 误差在10ms以内
    • 如果我们要保证run那段程序的原子性,就推荐使用温和的方式中断线程

等待线程 join()

  • 有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。
  • join方式使用时会让线程进入阻塞, 而且是如A执行B.join 会让A进入阻塞 B线程会正常运行, 直到B执行完, A才会执行
public class ThreadTest6 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("我是张三");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        };
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 3; i++) {
                    System.out.println("我是李四");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        thread.start();
        thread.join();
        thread1.start();
    }
}

我是张三
我是张三
我是张三
我是李四
我是李四
我是李四

获取当前线程的引用 currentThread()

  • 使用Thread.currentThread() 相当于获取当前线程的实例
System.out.println(Thread.currentThread().getName() + "在打游戏");

休眠线程 sleep()

  • 有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证休眠时间是大于等于休眠时间的。
  • 方法
    public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis 毫秒
    public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠

线程的状态

线程的所有状态

public class ThreadTest7 {
    public static void main(String[] args) {
        for (Thread.State s : Thread.State.values()
             ) {
            System.out.println(s);
        }
    }
}

NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED

线程状态的意义

  • NEW
    Thread对象有了,但是内核中的PCB对象还没有,就是任务布置了,还没有开始执行
  • RUNNABLE
    就绪状态, 正在CPU上执行,或者在就绪队列上等待执行
  • 接下来三种都是线程属于阻塞时的状态, 不会在CPU上执行,等时机成熟有机会才会进入就绪状态执行
  • BLOCKED
    等待锁
  • WAITING
    wait导致
  • TIMED_WAITING
    sleep导致
  • TERMINATED
    内核中线程已经结束PCB没了,但是thread对象还在等待GC来回收
  • 所以有一点较重要, isAlive是判断线程是否存活,也就是判断PCB是否存活, 所以只有中间四种状态才会显示true

线程状态的转移

在这里插入图片描述

  • 还有一种状方式是 yield() 大公无私,让出 CPU, 但是yield() 只是让出 CPU,并不会改变自己的状态

猜你喜欢

转载自blog.csdn.net/Shangxingya/article/details/106600760