3. 控制线程
书接上回,之前我们了解线程的创建和线程的状态,这一篇我们来了解一下线程的 API,在线程中控制线程状态的方法。主要有 sleep 休眠,yield 让步,jion 插入,以及线程的优先级,下面将逐一介绍。
3.1 sleep
线程休眠,也就是让当前的线程暂停一段时间,进入阻塞状态,等待定时结束则继续运行。
主要有两个方法:
- static native void sleep(long millis)
- static void sleep(long mills, int nanos)
//Thread.java
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && millis < Long.MAX_VALUE) {
millis++;
}
sleep(millis);
}
通过上述源码可以发现后者的方法实现也是通过调用前者实现。而且后者会对 nanos 的取值范围做了限制。因为 sleep 方法是一个 native 方法,这里我们先不研究,但是需要知道的是由于操作系统和线程调度器的原因,这里的毫秒值会存在精度问题。
3.2 yield
线程让步,当前线程向程序调度器提示当前线程可以放弃对处理器的使用,转换为就绪状态,并不会阻塞该线程。但是如果线程调度器发现没有线程在竞争,或者其他等待的线程的优先级没有当前线程的优先级高,则会忽略该提示,当前的线程还会继续执行。所以可能存在当前线程调用了 yield 方法后,还会继续运行的情况。
同样的该方法也是一个 native 方法:
public static native void yield();
拓展
当前 cpu 已经发展到多核心,所以 yield 方法有时候并没有明显的触发。所以不建议用来控制并发线程
3.3 join
如果说 sleep yield 这两个方法是排队的时候看到老弱病残主动让位,join 就相当于是插队者了。即在当前 A线程运行状态下,调用了B线程的 join 方法,则会阻塞A 线程,运行 B 线程,当 B 线程结束后 A 线程则继续运行。当然这里面可能存在看不惯插队者的行为的正义人士
,所以会有一个时间限制,当定时结束,如果 B 线程还没有执行完成,则不等待,A 继续执行。
join有三个使用方式分别为:
- join(long millis)
- join(long millis,int nanos)
- join()
// Thread.java
public final synchronized void join(final long millis)
throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay);
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
}
} else if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
public final synchronized void join(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && millis < Long.MAX_VALUE) {
millis++;
}
join(millis);
}
public final void join() throws InterruptedException {
join(0);
}
通过上面源码可以发现,三个方法最终都是调用到了当前对象的wait()方法,最大区别也就是等待时间上的不同。关于 object 的 wait/notify/notifyall 等方法,这里先留一个伏笔后续介绍锁的时候会继续讲解。
3.4 设置线程优先级
在之前的 yield 方法中提到了线程的优先级,线程的优先级确保了在多线程情况下执行的顺序。优先级高的线程,则会获取多的执行机会,而优先级低的则机会就相对少
默认情况下,每一个被创建出来的线程,都与创建线程的线程具有相同的优先级,这里可能看着有点绕,简单说 A 线程创建 B 线程,那么在不设置优先级的情况下,A 和 B 具有相同的优先级。默认情况:main 线程具有普通优先级
那么怎么设置线程的优先级呢?我们可以通过 Thread 类中的方法 setPriority(int newPriority)
来设置优先级。该 int 值的取值范围为 1-10,数字越大优先级越高。下面是 Thread 类中已有的三个线程优先级的静态常量
/**
* The minimum priority that a thread can have.
*/
public static final int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public static final int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public static final int MAX_PRIORITY = 10;