Java多线程(二):线程的常用方法和状态

目录

1.线程常用方法

1.1 线程终止

1.1.1 自定义标记符 

1.1.2 使用 Interrupt()

1.2 线程等待 join—李四接班

1.3 yield 让出执行权

1.4 获取当前线程

1.5 休眠当前线程 

1.5.1 使用 sleep 休眠

1.5.2 使用 TimeUnit 休眠

2.线程状态

2.1 所有的线程状态

2.2 线程状态转变


1.线程常用方法

1.1 线程终止

李四⼀旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如老板突然来电话了,说转账的对⽅是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉及到我们的停止线程的方式了。

目前常见的有以下两种方式:

  1. 通过 自定义标记符 来进行中断。
  2. 调用 interrupt() 方法来中断。

1.1.1 自定义标记符 

    // 1.先声明一个标识符
    private volatile static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (flag) {
                System.out.println("正在交易......");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("快溜~~~");
        });
        thread.start();
        Thread.sleep(3000);
        // 终止线程
        System.out.println("有内鬼,终止交易!");
        flag = false;
    }

运行结果:

1.1.2 使用 Interrupt()

        使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定义标志位.

使用 thread 对象的 interrupted() 方法通知线程结束: 

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (!Thread.interrupted()) {
                try {
                    Thread.sleep(TimeUnit.SECONDS.toMillis(1));
                    System.out.println("我正在转账");
                } catch (InterruptedException e) {
                    break;
                }
            }
            System.out.println("啊?险些误了⼤事");
        }, "⼦线程");
        t1.start();
        Thread.sleep(2200);
        System.out.println("停⽌转账有内⻤");
        t1.interrupt();
    }

运行结果:

注意interrupt() 需要配合 Thread.interrupted()Thread.currentThread().isInterrupted() 一块使用,从而实现线程终止。

isInterrupted VS interrupted:

  • interrupted:判断当前线程的中断标志位是否设置,调用后清除标志位。
  • isInterrupted:判断对象关联的线程的标志位是否设置,调用后不清除标志位。

对比代码如下: 

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            System.out.println("终止标志位:" + Thread.currentThread().isInterrupted());
            while (!Thread.interrupted()) {
                System.out.println("正在交易.....");
            }
            System.out.println("啊?险些误了大事!");
            System.out.println("终止标志位2:" + Thread.currentThread().isInterrupted());
            System.out.println("终止标志位2:" + Thread.currentThread().isInterrupted());
            System.out.println("终止标志位2:" + Thread.currentThread().isInterrupted());
            System.out.println();
            System.out.println("终止标志位4:" + Thread.interrupted());
            System.out.println("终止标志位4:" + Thread.interrupted());
            System.out.println("终止标志位4:" + Thread.interrupted());
        });
        thread.start();
        Thread.sleep(100);

        // 终止线程
        thread.interrupt();

        System.out.println("有内鬼,终止交易!");
    }

运行结果:

停止线程的 3 种方法https://juejin.cn/post/7069920237558169613 

1.2 线程等待 join—李四接班

         有时,我们需要等待⼀个线程完成它的工作后,才能进行自己的下⼀步工作。例如,李四只有等张三下班了他才上班,这时我们需要⼀个方法明确等待线程的结束。

 

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            // 张三开始上班
            System.out.println("1.张三开始上班:" + LocalDateTime.now());
            // 张三正在上班
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 张三下班
            System.out.println("3.张三下班:" + LocalDateTime.now());
        });
        // 启动线程
        t1.start();

        // 等待线程1执行完之后再执行后面的代码
        t1.join();

        Thread t2 = new Thread(() -> {
            // 李四开始上班
            System.out.println("1.李四开始上班:" + LocalDateTime.now());
            // 李四正在上班
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 李四下班
            System.out.println("3.李四下班:" + LocalDateTime.now());
        });
        // 启动线程
        t2.start();
    }

运行结果:

1.3 yield 让出执行权

我们可以使用 yield 让出执行权,如下代码所示:

    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            // 得到当前线程
            Thread t = Thread.currentThread();
            for (int i = 0; i < 100; i++) {
                // 让出CPU执行权
                Thread.yield();
                System.out.println("执行了线程" + t.getName());
            }
        }, "张三");
        thread.start();

        new Thread(() -> {
            // 得到当前线程
            Thread t = Thread.currentThread();
            for (int i = 0; i < 100; i++) {
                System.out.println("执行了线程" + t.getName());
            }
        }, "李四").start();
    }

运行结果:

可以看到,使用 yield 时, 张三的数量远远少于李四 。

结论yield 不改变线程的状态, 但是会重新去排队,而排队之后选择谁是不确定的。

1.4 获取当前线程

 这个方法我们已经很熟悉了,下面看一个例子:

    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }

运行结果:

当前线程为主线程。

1.5 休眠当前线程 

休眠线程有两种实现:

  • 使用 sleep 休眠
  • 使用 TimeUnit 休眠

1.5.1 使用 sleep 休眠

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(60 * 1000);
            } catch (InterruptedException e) {
                System.out.println("接受到了线程终止的通知");
            }
        });
        thread.start();

        Thread.sleep(1000);
        System.out.println("终止子线程");
        thread.interrupt();
    }

 运行结果:

1.5.2 使用 TimeUnit 休眠

TimeUnit.DAYS.sleep(1);//天
TimeUnit.HOURS.sleep(1);//⼩时
TimeUnit.MINUTES.sleep(1);//分
TimeUnit.SECONDS.sleep(1);//秒
TimeUnit.MILLISECONDS.sleep(1000);//毫秒
TimeUnit.MICROSECONDS.sleep(1000);//微妙
TimeUnit.NANOSECONDS.sleep(1000);//纳秒

下面看看源码: 

 

 可以看到 TimeUnit 底层也是通过 sleep 实现的。

2.线程状态

2.1 所有的线程状态

        for (Thread.State item : Thread.State.values()) {
            System.out.println(item);
        }

可以看到总共有六种状态:

  • NEW(新建): 线程被创建,但是还未启动(start)
  • RUNNABLE(运行): 可工作的.,又可以分成正在工作中和即将开始工作
  • BLOCKED(阻塞): 如果遇到锁,线程就会变为阻塞状态,等待另一个线程释放锁
  • WAITING(等待): 无限期等待
  • TIMED_WAITING(超时等待): 有明确结束时间的等待状态
  • TERMINATED(终止): 线程结束完成

2.2 线程状态转变

 这个图看着挺复杂,下面用一个例子加以解释:

         刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;

        当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行工作⼈员开始接待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度

         当李四、王五因为⼀些事情需要去忙,例如需要填写信息、回家取证件、发呆⼀会等时,进入 BLOCKEDWATINGTIMED_WAITING 状态。

        如果李四、王五已经忙完,为 TERMINATED 状态。

观察线程状态的转换:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            Thread t = Thread.currentThread();
            System.out.println("线程状态2:" + t.getState());
        });
        System.out.println("线程状态1:" + thread.getState());
        thread.start();
        Thread.sleep(500);
        System.out.println("线程状态3:" + thread.getState());
    }

运行结果:

更多线程状态转换的示例https://juejin.cn/post/7065856076737937438

猜你喜欢

转载自blog.csdn.net/m0_59140023/article/details/124274762