Thread 类及常见方法

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。

每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。

1 Thread 的常见构造方法

方法

说明

Thread()

创建线程对象

Thread(Runnable target)

使用 Runnable 对象创建线程对象

Thread(String name)

创建线程对象,并命名

Thread(Runnable target, String name)

使用 Runnable 对象创建线程对象,并命名

【了解】Thread(ThreadGroup group, Runnable target)

线程可以被用来分组管理,分好的组即为线程组,这个目前我们了解即可

Thread t1 = new Thread();

Thread t2 = new Thread(new MyRunnable()); Thread t3 = new Thread("这是我的名字");

Thread t4 = new Thread(new MyRunnable(), "这是我的名字");

2 Thread 的几个常见属性

属性

获取方法

ID

getId()

名称

getName()

状态

getState()

优先级

getPriority()

是否后台线程

isDaemon()

是否存活

isAlive()

是否被中断

isInterrupted()

ID 是线程的唯一标识,不同线程不会重复

名称是各种调试工具用到

状态表示线程当前所处的一个情况,下面我们会进一步说明

优先级高的线程理论上来说更容易被调度到

关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。

是否存活,即简单的理解,为 run 方法是否运行结束了

线程的中断问题,下面我们进一步说明

public class ThreadDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    System.out.println(Thread.currentThread().getName() + ": 我还
活着");
                    Thread.sleep(1 * 1000);
               } catch (InterruptedException e) {
                    e.printStackTrace();
               }
           }
            System.out.println(Thread.currentThread().getName() + ": 我即将死去");
       });
        System.out.println(Thread.currentThread().getName() 
                           + ": ID: " + thread.getId());
        System.out.println(Thread.currentThread().getName() 
                           + ": 名称: " + thread.getName());
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
        System.out.println(Thread.currentThread().getName() 
                           + ": 优先级: " + thread.getPriority());
        System.out.println(Thread.currentThread().getName() 
                           + ": 后台线程: " + thread.isDaemon());
        System.out.println(Thread.currentThread().getName() 
                           + ": 活着: " + thread.isAlive());
        System.out.println(Thread.currentThread().getName() 
                           + ": 被中断: " + thread.isInterrupted());
        thread.start();
        while (thread.isAlive()) {}
        System.out.println(Thread.currentThread().getName() 
                           + ": 状态: " + thread.getState());
   }
}

3 启动一个线程-start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

覆写 run 方法是提供给线程要做的事情的指令清单线程对象可以认为是把李四、王五叫过来了。而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了。

调用 start 方法, 才真的在操作系统的底层创建出一个线程.

4 中断一个线程

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

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

1. 使用标志位来控制线程是否要停止

public class ThreadDemo8 {
    private static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (flag) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();

        Thread.sleep(3000);
        // 在主线程里就可以随时通过 flag 变量的取值, 来操作 t 线程是否结束.
        flag = false;
    }
}

2. 使用 Thread 自带的标志位来进行判定

public class ThreadDemo9 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);

        t.interrupt();
    }
}

清除标志位可以把选择权交给程序猿自己,选终止还是继续执行

5 等待一个线程 - join()

        有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决定是否存钱,这时我们需要一个方法明确等待线程的结束。主线程main需要等 t 线程执行完才会继续执行

public class ThreadDemo10 {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t.start();

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("join 之前");

        // 此处的 join 就是让当前的 main 线程来等待 t 线程执行结束 (等待 t 的 run 执行完)
        try {
            t.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("join 之后");
    }
}

        如果执行 join 的时候,t 已经结束了。这是 join 不会阻塞,就会立即返回。即线程没结束,等待;线程结束了,立即返回。

方法

说明

public void join()

等待线程结束(一直等)

public void join(long millis)

等待线程结束,最多等 millis 毫秒

public void join(long millis, int nanos)

同理,但可以更高精度

6 获取当前线程引用

这个方法我们非常熟悉了

方法

说明

public static Thread currentThread();

返回当前线程对象的引用

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // currentTimeMillis 获取当前的时间戳
        System.out.println(System.currentTimeMillis());
        Thread.sleep(3 * 1000);
        System.out.println(System.currentTimeMillis());
    }
}

7 休眠当前线程

        因为线程的调度是不可控的,所以,这个方法只能保证实  际休眠时间是大于等于参数设置的休眠时间的。

方法

说明

public static void sleep(long millis) throws InterruptedException

休眠当前线程 millis 毫秒

public static void sleep(long millis, int nanos) throws InterruptedException

可以更高精度的休眠

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());
    }
}

        线程 A 调用 sleep ,A 就会进入休眠状态,把 A 从就绪状态链表中拎出来放到阻塞链表中。比如调用 sleep(1000),对应的线程 PCB 就要在阻塞队列中待1000ms;但是实际上要考虑调度的开销,是无法唤醒后就立即执行的,实际上的时间间隔大概率要大于1000ms

相关推荐

  1. Python中class方法的用法详解常见

    2024-03-30 22:10:05       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-30 22:10:05       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-30 22:10:05       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-30 22:10:05       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-30 22:10:05       20 阅读

热门阅读

  1. 数据可视化之极坐标

    2024-03-30 22:10:05       17 阅读
  2. 安卓APP开发中的重要环节:数据和文件存储概述

    2024-03-30 22:10:05       20 阅读
  3. Golang- 邮件服务,发送邮件

    2024-03-30 22:10:05       21 阅读
  4. 【Docker】常用命令 docker compose

    2024-03-30 22:10:05       16 阅读
  5. C语言- %i 读取不同进制的数

    2024-03-30 22:10:05       14 阅读
  6. 2023春秋杯冬季赛wp

    2024-03-30 22:10:05       21 阅读
  7. uniapp 未配置appkey或配置错误的解决

    2024-03-30 22:10:05       16 阅读
  8. Doris案例篇—Doris 在思必驰的应用实践

    2024-03-30 22:10:05       19 阅读
  9. kubelet源码阅读

    2024-03-30 22:10:05       20 阅读
  10. 【软考---系统结构设计师】IPv6

    2024-03-30 22:10:05       21 阅读