Thread的API详细介绍(一)

本文详细介绍了Java中线程的休眠(sleep)、yield方法的使用及区别,以及线程优先级的设置和获取。强调了Thread.sleep方法的精确度和TimeUnit的便捷性,解释了yield方法仅是提示调度器让出CPU资源的性质。同时,文章探讨了线程优先级的实际效果和限制,指出不应过度依赖优先级。此外,还提到了获取线程ID和当前线程的方法,并简述了线程上下文类加载器的作用。

方法名static功能说明注意
start启动一个新线 程,在新的线程 运行 run 方法 中的代码start 方法只是让线程进入就绪,里面代码不一定立刻 运行(CPU 的时间片还没分给它)。每个线程对象的 start方法只能调用一次,如果调用了多次会出现 IllegalThreadStateException
run()新线程启动后会 调用的方法如果在构造 Thread 对象时传递了 Runnable 参数,则 线程启动后会调用 Runnable 中的 run 方法,否则默 认不执行任何操作。但可以创建 Thread 的子类对象, 来覆盖默认行为
join()等待线程运行结 束
join(long n)等待线程运行结 束,最多等待 n 毫秒
getId()获取线程长整型 的 idid 唯一
getName()获取线程名
setName(String)设置线程名
getPriority()获取线程优先级
setPriority(int)设置线程优先级java中规定线程优先级是1~10 的整数,较大的优先级 能提高该线程被 CPU 调度的机率
getState()获取线程状态Java 中线程状态是用 6 个 enum 表示,分别为: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
isInterrupted()判断是否被打 断,不会清除 打断标记
isAlive()线程是否存活 (还没有运行完 毕)
interrupt()打断线程如果被打断线程正在 sleep,wait,join 会导致被打断 的线程抛出 InterruptedException,并清除 打断标 记 ;如果打断的正在运行的线程,则会设置 打断标 记 ;park 的线程被打断,也会设置 打断标记
interrupted()static判断当前线程是 否被打断会清除 打断标记
currentThread()static获取当前正在执 行的线程
sleep(long n)static让当前执行的线 程休眠n毫秒, 休眠时让出 cpu 的时间片给其它 线程
yield()static提示线程调度器 让出当前线程对 CPU的使用主要是为了测试和调试

1 线程的休眠(sleep)

sleep是一个静态方法,其有两个重载方法,其中一个需要传入毫秒数,另外一个既需要毫秒数也需要纳秒数。

1.1 sleep方法介绍

public static void sleep(long millis)throws InterruptedException;

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

sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定了一个休眠的时间,但是最终要以系统的定时器和调度器的精度为准,休眠有一个非常重要的特性,那就是其不会放弃monitor锁的所有权

1.2 使用TimeUnit替代Thread.sleep

在JDK1.5以后,JDK引入了一个枚举TimeUnit,其对sleep方法提供了很好的封装,使用它可以省去时间单位的换算步骤,比如线程想休眠3小时24分17秒88毫秒,使用TimeUnit来实现就非常的简便优雅了

Thread.sleep(12257088L);
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(24);
TimeUnit.SECONDS.sleep(17);
TimeUnit.MILLISECONDS.sleep(88);

同样的时间表达,TimeUnit显然清晰很多,建议使用TimeUnit来代替.

2 线程的 yield

2.1 yield方法介绍

yield方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前的CPU资源,如果CPU的资源不紧张,则会忽略这种提醒

调用yield方法会使当前线程从RUNNING状态切换到RUNNABLE状态,一般这个方法不太常用

2.2 yield和sleep

在JDK1.5以前的版本中yield的方法事实上是调用了sleep(0),但是它们之间存在着本质的区别,具体如下

  • sleep会导致当前线程暂停指定的时间,没有CPU时间片的消耗。
  • yield只是对CPU调度器的一个提示,如果CPU调度器没有忽略这个提示,它会导致线程上下文的切换。
  • sleep会使线程短暂block,会在给定的时间内释放CPU资源。
  • yield会使RUNNING状态的Thread进入RUNNABLE状态(如果CPU调度器没有忽略这个提示的话)。
  • sleep几乎百分之百地完成了给定时间的休眠,而yield的提示并不能一定担保。
  • 一个线程sleep另一个线程调用interrupt会捕获到中断信号,而yield则不会。

3 设置线程的优先级

// 为线程设定优先级。
public final void setPriority(int newPriority);
// 获取线程的优先级。
public final int getPriority();

3.1 线程优先级介绍

理论上是优先级比较高的线程会获取优先被CPU调度的机会,但是事实上往往并不会如你所愿,设置线程的优先级同样也是一个hint操作:

  • 对于root用户,它会hint操作系统你想要设置的优先级别,否则它会被忽略。
  • 如果CPU比较忙,设置优先级可能会获得更多的CPU时间片,但是闲时优先级的高低几乎不会有任何作用。
  • java中规定线程优先级是1~10 的整数,较大的优先级能提高该线程被 CPU 调度的机率

不要在程序设计当中企图使用线程优先级绑定某些特定的业务,或者让业务严重依赖于线程优先级

  1. 默认优先级别是5
Thread t1 = new Thread();
System.out.println(t1.getPriority());  // 5

3.2 线程优先级源码分析

设置线程的优先级,只需要调用setPriority方法即可,下面我们打开Thread的源码:

// 最小优先级
public final static int MIN_PRIORITY = 1;

// 默认优先级
public final static int NORM_PRIORITY = 5;

// 最大优先级
public final static int MAX_PRIORITY = 10;
public final void setPriority(int newPriority) {
  ThreadGroup g;
  checkAccess();
  if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
      throw new IllegalArgumentException();
  }
  if((g = getThreadGroup()) != null) {
  		// 如果设置的优先级大于所属线程组的最大优先级,
  		// 就不会采用设置的值,而是采用线程组最大的优先级
      if (newPriority > g.getMaxPriority()) {
          newPriority = g.getMaxPriority();
      }
      // setPriority0 本地方法
      setPriority0(priority = newPriority);
  }
}
 private native void setPriority0(int newPriority);
  • 线程的优先级不能小于1也不能大于10
  • 如果指定的线程优先级大于线程所在group的优先级,那么指定的优先级将会失效,取而代之的是group的最大优先级。

测试:发现线程最终的优先级不是8而是6

public static void main(String[] args) {
    // 定义一个线程组
    ThreadGroup threadGroup = new ThreadGroup("test线程组");
    // 设置线程组的最大优先级
    threadGroup.setMaxPriority(6);

    // 创建一个线程,并加入刚刚创建的线程组
    Thread thread = new Thread(threadGroup,"test-thead");
    // 设置线程的优先级为8,大于线程组最大优先级
    thread.setPriority(8);
    System.out.println(thread.getPriority()); // 6
}

3.3 关于优先级的一些总结

  • 一般情况下,不会对线程设定优先级别,更不会让某些业务严重地依赖线程的优先级别
  • 默认优先级别是5,这句话其实不对,其实是线程默认的优先级和它的父类保持一致,一般情况下都是5,因为main线程的优先级就是5,所以它派生出来的线程都是5
    测试:
public static void main(String[] args) {
    // 创建一个线程
    Thread thread1 = new Thread();
    // 主线程
    Thread main = Thread.currentThread();
    // 设置主线程的优先级
    main.setPriority(7);

    // 创建一个线程
    Thread thread2 = new Thread();
  
    System.out.println("thread1的优先级:" + thread1.getPriority());
    System.out.println("thread2的优先级:" + thread2.getPriority());
}
thread1的优先级:5
thread2的优先级:7

发现当设置主线程的优先级为7的时候,后来创建的线程thread2的优先级也是7了(线程默认的优先级和它的父类保持一致)

查看构造方法中调用的init方法中,里面就有这么一行代码,设置线程的默认优先级就是父线程的优先级

Thread parent = currentThread();
// 设置线程的默认优先级就是父线程的优先级
this.priority = parent.getPriority();

线程的父子关系不懂的可以查看:线程构造函数分析

4 获取线程id

线程的ID在整个JVM进程中都会是唯一的,并且是从0开始逐次递增。

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

运行发现并不是从0开始的,这是为啥呢?在一个JVM进程启动的时候,实际上是开辟了很多个线程,比如垃圾回收线程

5 获取当前线程

Thread类内部提供了一个静态的本地方法用于获取用于返回当前执行线程的引用

 public static native Thread currentThread();
public static void main(String[] args) {

    Thread thread = new Thread("test-thread") {
        @Override
        public void run() {
            //always true
            System.out.println(Thread.currentThread() == this);
            System.out.println(Thread.currentThread().getName());
        }
    };
    thread.start();
}
true
test-thread
public static void main(String[] args) {

    Thread thread = new Thread("test-thread") {
        @Override
        public void run() {
            //always true
            System.out.println(Thread.currentThread() == this);
            System.out.println(Thread.currentThread().getName());
            // 所以上面这个地方也可以写成这样了:this此时就是当前线程
            System.out.println(this.getName());
        }
    };
    thread.start();
}

6 设置线程上下文类加载器(先了解)

// 获取线程上下文的类加载器,简单来说就是这个线程是由哪个类加器加载的,如果是在没有修改线程上下文类加载器的情况下,则保持与父线程同样的类加载器。
public ClassLoader getContextClassLoader();
// 设置该线程的类加载器,这个方法可以打破JAVA类加载器的父委托机制,有时候该方法也被称为JAVA类加载器的后门。
public void setContextClassLoader(ClassLoader cl)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值