哈喽,小伙伴们,大家好!今天给大家分享的是Thread里面重要的方法,为了便于大家掌握,我分为三部分进行讲解,首先是第一部分,这些方法是在开发中比较常用的API,常用的和不常用的,我都会给大家提示,希望大家都有所收获,好了,进入正题。
1 sleep()方法(常用)
sleep 是一个静态方法,其有两个重载方法,其中一个需要传入毫秒数,另外一个既需 要毫秒数也需要纳秒数。
1.1 方法介绍以及实现
sleep 方法会使当前线程进入指定毫秒数的休眠,暂停执行,虽然给定了一个休眠的时间, 但是最终要以系统的定时器和调度器的精度为准,休眠有一个非常重要的特性,那就是其不会放弃 monitor 锁的所有权。下面举个例子进行说明:
public class ThreadSleep {
public static void main(String[] args) {
// 子线程
new Thread(() -> {
long startTime = System.currentTimeMillis();
sleep(2000L);
long endTime = System.currentTimeMillis();
System.out.printf("Total spend %d ms\n", endTime - startTime);
}).start();
// main 线程
long startTime = System.currentTimeMillis();
sleep(3000L);
long endTime = System.currentTimeMillis();
System.out.printf("Main thread spend %d ms", endTime - startTime);
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我们分别在自定义的线程和主线程中进行了休眠,每个线程的休眠互不影响,从结果看,Thread.sleep 只会导致当前线程进入指定时间的休眠。
1.2 使用 TimeUniT 替代 Thread.sleep
在 JDK1.5 以后,JDK 引入了一个枚举 TimeUnit,其对 sleep 方法提供了很好的封装, 使用它可以省去时间单位的换算步骤,比如线程想休眠 3 小时 24 分 17 秒 88 毫秒,使用 TimeUnit 来实现就非常简便优雅了:
public static void main(String[] args) {
try {
// 第一种方式:Thread.sleep
Thread.sleep(12257088L);
// 第二种方式:TimeUnit.HOURS.sleep
TimeUnit.HOURS.sleep(3);
TimeUnit.MINUTES.sleep(24);
TimeUnit.SECONDS.sleep(17);
TimeUnit.MILLISECONDS.sleep(88);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
同样的时间表达,TimeUnit 显然清晰很多,强烈建议在使用 Thread.sleep 的地方, 完全使用 TimeUnit 来代替,因为 sleep 能做的事情,TimeUnit 全部都能完成,并且可以做的更好。
1.3 Thread.sleep(0)
Thread.sleep(0) 表示挂起 0 毫秒,但是Thread.sleep(0) 并非是真的要线程挂起 0 毫秒,目的
在于这次调用 Thread.sleep(0)的当前线程确实的被冻结了一下,让其他线程有机会优先执行。Thread.sleep(0)是你的线程暂时放弃 cpu,也就是释放一些未用的时间片给其他线程或进程使用,就相当于一个让位动作。
在线程中,调用 sleep(0)可以释放 cpu 时间,
让线程马上重新回到就绪队列而非等待队列,
sleep(0)释放当前线程所剩余的时间片(如果有剩余的话),这样可以让操作系 统切换其他线程来执行,提升效率。
2 yield()方法(不常用)
yield 方法属于一种启发式的方法,其会提醒调度器我愿意放弃当前的 CPU 资源,如果 CPU 的资源不紧张,则会忽略这种提醒。调用 yield 方法会使当前线程从 Running 状态切换到 Runnable 状态,因为这个方法是不可控的,所以一般这个方法我们不会常用。所以这里我代码就不贴上来了,就讲一下yield方法和sleep方法的区别。在 JDK1.5 以前的版本中 yield 的方法事实上是调用了 sleep(0),但是他们之间存在着本质的区别,具体如下:
- sleep 会导致当前线程暂停指定的时间,没有 CPU 时间片的消耗。
- yield 只是对 CPU 调度器的一个提示,如果 CPU 调度器没有忽略这个提示,它会导致线程上下文的切换。
- sleep 会使线程短暂 block,会在给定的时间内试放 CPU 资源
- yield 会使 Running状态的Thread 进入Runnable 状态(如果CPU调度器没有忽略这个提示的话)
- sleep 几乎百分之百的完成了给定时间的休眠,而 yield 的提示并不能一定保证
3 setPriority和getPriority方法(不常用)
设置线程的优先级,只需要调用 setPriority 方法即可。
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(priority = newPriority);
}
}
通过上面源码的分析,我们可以看出,线程的优先级不能小于1也不能大于10,如果指定的线程优先级大于线程所在 group 的优先级,那么指定的优先级将会失败,取而代之的是 group 的最大优先级。下面的代码会给我们证实:
public class ThreadPriority02 {
public static void main(String[] args) {
// 定义一个线程组
ThreadGroup testGroup = new ThreadGroup("testGroup");
// 将线程组的优先级指定为 7
testGroup.setMaxPriority(7);
// 定义一个线程,将该线程加入到 group 中
Thread testThread = new Thread(testGroup, "testThread");
// 企图将线程的优先级设定为 10
testThread.setPriority(10);
// 未设置成功 输出:7
System.out.println(testThread.getPriority());
}
}
一般情况下,不会对线程设定优先级别,更不会让某些业务严重的依赖线程的优先级别, 比如权重,借助优先级设定某个任务的权重,这种方式是不可取的,一般定义线程的时候使用默认的优先级就好了, 线程默认的优先级和它的父类保持一致,一般情况下都是 5,因为 main 线程的优先级就是 5,所以它派生出来的线程都是 5。