sleep是Thread的静态方法,作用是使当前线程“睡眠”一定时间,期间线程不会释放对象锁。
public static native void sleep(long millis) throws InterruptedException;
join是Thread的普通方法,作用是等待调用join方法的线程死亡,才能接着执行当前线程的后续方法。例如,常用于主线程需要等待子线程结束后,才能执行主线程中join方法之后的代码的场景。
public final void join() throws InterruptedException {
join(0);
}
join和sleep从使用效果上来看,都能使线程处于“阻塞”状态,两者的主要区别是,sleep睡眠期间不会释放对象锁,像一个占有欲很强的小孩,睡觉了还死死地抱着布偶娃娃。join因为内部实现使用了wait方法,当前线程所持有的锁会被释放。从源码分析,
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); --------wait()会释放对象锁
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay); --------wait()会释放对象锁
now = System.currentTimeMillis() - base;
}
}
}
理论是枯燥的,举个对比的例子:
public class TestJoinAndSleep {
static class Service extends Thread {
public synchronized void method() {
System.out.println("method " + ", time = " + System.currentTimeMillis());
}
@Override
public void run() {
System.out.println("run begin" + ", time = " + System.currentTimeMillis());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run end" + ", time = " + System.currentTimeMillis());
}
}
static class ThreadA extends Thread {
Service service;
ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
try {
synchronized (service) {
service.start();
Thread.sleep(6000); //注意这里
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
static class ThreadB extends Thread {
Service service;
ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.method();
}
}
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
}
执行后的结果如下:ThreadA在run方法中持有Service对象的锁,在sleep(6000)后释放对象锁,因为ThreadB要在6秒后才能拿到Service的那一把锁,才能执行method方法
run begin, time = 1596982444974
run end, time = 1596982449978
method , time = 1596982450981 //和run begin差6秒
将ThreadA run方法中的sleep替换为join,输出如下,method和run begin相差1s,在ThreadA的run方法join执行后,释放锁,b线程抢到了锁,执行method方法。
public void run() {
try {
synchronized (service) {
service.start();
//Thread.sleep(6000);
service.join();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String str = new String();
Math.random();
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
run begin, time = 1596982735081
method , time = 1596982736098
run end, time = 1596982740091