Thread类的常用方法

文章目录

  • 二. Thread类及常见方法
    • 2.1 常见构造方法
    • 2.2 Thread 的几个常见属性
    • 2.3 启动一个线程 start()
    • 2.4 终止一个线程
    • 2.5 等待一个线程 join()
    • 2.6 获取当前线程的引用
    • 2.7 休眠当前线程

二. Thread类及常见方法

2.1 常见构造方法

方法 说明
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(new Runnable() {
    @Override
    public void run() {
		//
    }
});

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

Thread t5 = new Thread(new MyRunnable(), "这是我的名字");
Thread t6 = new Thread(new Runnable() {
    @Override
    public void run() {
		//
    }
}, "这是我的名字");

2.2 Thread 的几个常见属性

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
是否存活 isAlive()
是否被中断 isInterrupted()
  • ID是线程的身份标识(JVM给线程设定的标识)
  • 名称是各种调试工具用到
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程理论上来说更容易被调度到
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
    • 后台线程也称为守护线程, 不影响进程的结束
    • 前台线程, 会影响到进程的结束, 如果前台线程没执行完进程, 是不会结束的.
    • 一个进程中所有的前台线程都执行完并退出了, 即使后台线程没有执行完, 也会跟着进程一起推出.
  • 是否存活,即简单的理解,为 run 方法是否运行结束了

2.3 启动一个线程 start()

调用start()方法才能在操作系统中创建出一个线程.

调用完start方法后, 代码会立即继续执行start后续的逻辑.

2.4 终止一个线程

当一个线程的run方法执行完毕, 线程就算终止了. 此处的终止线程, 就是让run能够尽快的执行完毕.

如何使其尽快结束?

  • 手动设定标志位.

    public class Demo {
        public static boolean isQuit = false;//标志位
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() -> {
                while (!isQuit) {
                    System.out.println("Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            });
            t.start();
            //主线程这里执行一些其他逻辑之后, 让t线程结束
            
            Thread.sleep(3000);
            isQuit = true;//修改标志位
            System.out.println("t线程终止");
        }
    }
    

    Thread类的常用方法_第1张图片

    如果我们把代码改成这样

    public class Demo {
        public static void main(String[] args) throws InterruptedException {
            boolean isQuit = false;//标志位
            Thread t = new Thread(() -> {
                while (!isQuit) {
                    System.out.println("Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            });
            t.start();
            //主线程这里执行一些其他逻辑之后, 让t线程结束
            
            Thread.sleep(3000);
            isQuit = true;//修改标志位
            System.out.println("t线程终止");
        }
    }
    

    编译器会报错, 为什么?

    因为我们创建线程借用的是lambda表达式, 他是一个回调函数, 只有时机到了才会执行, 所以他的执行时间是靠后的, 这就导致后续真正执行lambda的时候, 局部变量isQuit可能已经被摧毁了.

    所以让lambda去访问一个已经被销毁的变量是不合适的.

    lambda便引入了"变量捕获"这样的机制 : lambda内部看起来是在直接访问外部的变量, 其实本质上是吧外部的变量给复制一份到lambda里面, 这样就解决生命周期的问题了.

    但是, 变量捕获有个限制, 要求捕获的变量final的, 即不可变的. 而上述代码在主线程内修改了被捕获变量的值, 所以报错了.

    我们的第一种写法没有触发变量捕获, 而是内部类访问外部类的成员, 这种写法是合理的.

  • 使用 Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替自定

    义标志位.

    public class Demo {
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(() -> {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("Thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            t.start();
            Thread.sleep(3000);
            t.interrupt();
            System.out.println("0");
        }
    }
    
    • Thread.currentThread(): 获取到当前的Thread对象. 在代码中就相当于t, 为什么不直接用t呢? 因为lambda表达式是在构造t之前就定义好的, 如果直接用t就会使编译器认为他是一个还没初始化的对象.

    • isInterrupted(): Thread对象内部提供了一个标志位(boolean), 若方法返回true, 则表示线程应该结束, 若是false, 线程先不必结束.

    • interrupt(): 把标志为设置成true.

    • 如果运行上述代码, 会爆出一个异常, 并且循环不会终止

      在这里插入图片描述

      这个异常的意思是, 睡眠时被唤醒. 在被唤醒后, 标志位会被自动清除, 如果还想让线程停止, 直接在catch里面加个break即可.

    • 当sleep被唤醒后, 可以有以下几种操作方式

      • 立即结束线程: 在catch中直接break.
      • 继续做其他事情, 一会在结束线程: 在catch中执行别的逻辑, 执行完了再break.
      • 忽略终止请求, 继续执行该线程: 不写break.

2.5 等待一个线程 join()

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

方法 说明
public void join() 等待线程结束
public void join(long millis) 等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos) 同理,但可以更高精度

a, b两个线程, 希望b先结束, a后结束, 就可以在a线程中调用b.join方法. 如果b县城还没执行完, a线程会进入阻塞状态. b执行完后, a再从阻塞状态中恢复回来, 继续往后执行.

public class Demo {
    public static void main(String[] args) {
        Thread b = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("b");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("b end");
        });
        Thread a = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("a");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            try {
                b.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("a end");
        });

        a.start();
        b.start();
    }
}

2.6 获取当前线程的引用

方法 说明
public static Thread currentThread(); 返回当前线程对象的引用

2.7 休眠当前线程

方法 说明
public static void sleep(long millis) throws InterruptedException 休眠当前线程 millis毫秒
public static void sleep(long millis, int nanos) throws InterruptedException 可以更高精度的休眠

你可能感兴趣的:(Javaee,java,jvm,开发语言)