目录
1、join() 方法:释放锁
使用场景:
等待调用 join() 方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之后才能继续运行的场景。
比如,主线程创建并启动子线程,如果自线程中要进行大量的耗时运算,主线程往往将早于子线程结束之前结束。如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到 join() 方法。
易混淆知识点:join() 方法为 Thread 类直接提供的方法,而 wait() 和 notify() 为Object类中的方法。
查看 join() 方法源码,底层最终调用的还是 wait() 方法:
join() 的主要作用是使当前主线程进行等待,另外也可以指定等待的具体时间:
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() { // 线程一
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 运行结束!");
}
}, "thread_1");
t1.start();
t1.join(); // 主线程会等待线程一运行结束
// t1.join(1000); // 超时将不再等待
System.out.println("程序运行结束...");
}
}
2、sleep() 方法:不释放锁
调用 sleep() 方法会让当前线程从 Running 进入TIMED_WAITING 状态,不会释放对象锁。
睡眠结束后的线程未必会立刻得到执行,当 sleep() 传入参数为 0 时,和 yield 相同。
sleep() 方法可以被中断,其它线程可以使用 interrupt() 方法打断正在睡眠的线程,这时 sleep() 方法会抛出 InterruptedException,并且会清除中断标志。
public class SleepTest {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000); // 会响应中断,并清除中断标记,不会睡眠 5s
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 执行结束!");
}
});
t1.start();
t1.interrupt(); // 打上中断标记
}
}
使用场景:当 A 需要等待 B 执行完,此时 A 设置 1 小时的等待时间(睡眠 1 小时),但是 B 只用了 30 分钟便执行完了,此时可以在 B 上执行 A 的中断方法,使 A 继续执行,因此,异常也是可以被利用的。
3、yeild() 方法:不释放锁
yield() 会释放 CPU 资源,让当前线程从 Running 进入 Runnable 状态,让优先级更高 (至少是相同)的线程获得执行机会,不会释放对象锁;但 CPU 再次调度还可能调度到本线程。
具体的实现依赖于操作系统的任务调度器
官方不建议使用这个方法,因为线程切换CPU是有代价的
/** <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
相关测试代码:
public class YieldTest extends Thread{
// 对象锁
public static Object lock = new Object();
@Override
public void run() {
synchronized (lock) {// 加锁
System.out.println(Thread.currentThread().getName() + "开始调用yield()方法");
// 提醒1:打开关闭此注释查看输出效果,对比差异
this.yield();
// 提醒2:使用wait方法来做对比,查看释放锁与不释放锁的区别
// try {
// lock.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(Thread.currentThread().getName() + " 执行结束");
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) { // 创建100个线程
YieldTest test = new YieldTest();
test.start();
}
// 提醒2:配合wait使用看效果
// synchronized (lock) {
// lock.notifyAll();
// }
}
}
至此,三种方法的区别介绍完毕。