多线程常见方法
sleep()
- 线程调用
sleep()方法会使线程变为TIMED_WAITING阻塞状态。 - 其它线程可以调用
interrupt()方法会打断正在睡眠线程,这时会抛出InterruptedException异常。 - 睡眠结束后的线程会进入RUNNABLE状态,不一定会立刻执行。
yield()
使线程从Running变为Runnable就绪状态,让出当前运行状态。此时调度器仍有可能调用此线程。
给调度程序的提示,表明当前线程愿意放弃其当前对处理器的使用。调度程序可以忽略这个提示。
setPriority(int newPriority)
设置线程的优先级(1~10),优先级高的被调度的可能性就越高.
/**
* 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;
wait()
- 线程进入WAITING状态,进入wait set等待,并让出获得的锁。
- 进入阻塞状态,不会占用CPU时间片。
- 需要利用
synchronized获得线程的锁后才可以调用该方法,如不获得锁就调用该方法会throw IllegalMonitorStateException。
示例代码
public class Main {
private static final Object object = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
synchronized (object) {
try {
System.out.println("进入wait!");
object.wait();
System.out.println("结束wait!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
Thread.sleep(1000);
// 需要利用 synchronize 获得锁!!!
synchronized (object) {
object.notifyAll();
}
}
}
notify() notifyAll()
该方法会唤醒 wait set 中的线程,notify()会随机唤醒一个,notifyAll()会唤醒wait set中的所有线程。
join()
当线程A执行thread.join()后线程A进入TIMED_WAITTING状态。
线程A会等待thread线程终止之后才从超时方法中返回,继续运行。
同时还有join(long millis)和join(long millis, int nanos)两个带超时方法,该方法会使线程A进入TIMED_WAITING状态,如果在给定的时间内thread线程没有终止,那么线程A将会从超时方法中返回,继续运行。
根据源码我们可以知道,join()的底层就是wait():
- 当传入
millis为 0 时,join()方法会一直调用isAlive()方法判断线程是否存活,如果存活就一直等待。 - 当传入
millis不为 0 时,当超出给出的超时时间时会break跳出循环。
// native method
public final native boolean isAlive();
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
interrupt()
中断可以理解为线程的一个标识属性,相当于其它线程通知该线程。有两种方法可以判断当前线程的打断状态:
Thread.interrupted()当使用该方法后,会清楚打断标志,此时再获取打断标志会得到的false。Thread.currentThread().isInterrupted()使用后不会改变打断标志。
注意事项:
- 当打断处于sleep wait join状态的线程时会throw异常,并且会使打断状态为
false,此时可以在catch块中再次调用interrupted()方法设置打断标记并自行打断。 - 打断处于运行状态的线程或自行打断,此时并不会直接暂停被打断的线程。被打断的线程可以通过
isInterrupted()方法获取线程的打断状态,再来决定接下来的操作。想比于被废弃的stop()方法,interrupt()可以在接下来中释放锁等资源,更加安全。
打断正常运行的线程
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("结束运行!");
break;
}
}
});
t.start();
// 5 秒后打断 t 线程
Thread.sleep(5000);
t.interrupt();
}
}
打断处于sleep wait join状态的线程
需注意在异常块中再次调用了interrup()方法。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
try {
if (Thread.currentThread().isInterrupted()) {
System.out.println("结束运行!");
break;
}
System.out.println("运行!");
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 再次调用,使得打断标志为true
e.printStackTrace();
}
}
});
t.start();
// 5 秒后打断 t 线程
Thread.sleep(5000);
t.interrupt();
}
}
LockSupport.park() & LockSupport.unpark()
unpark()不会释放锁,可以在线程暂停前调用,也可以在暂停后调用。
如果在park()前使用,则运行到park()时会直接跳过。