一、等待线程-join()
在我们平常写代码的 时候会遇到需要等待一个线程执行完毕的时候,再执行下一个线程,然而我们之前学过的sleep具有以下特性:
- 当前线程停止运行指定的毫秒数。时间一到,线程自动进入"就绪状态",等待 CPU 调度。
- 它不释放锁。如果当前线程持有了某个对象的同步锁(synchronized),在 sleep 期间,其他线程依然无法获取该锁。
所以我们不可能精准预测每一个线程执行所耗费的时间,为了避免这个问题,我们通常用join()函数。
public class demo3 {
public static void main(String[] args) throws InterruptedException {
Runnable target=()->{
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + " 我正在工作" );
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + " 工作结束");
};
Thread t1=new Thread(target,"张三");
Thread t2=new Thread(target,"李四");
System.out.println("先让李四开始工作");
t2.start();
t2.join();
System.out.println("李四工作结束了,让张三开始工作");
t1.start();
t1.join();
}
}
join函数的特性:
Thread t = new Thread(() -> { /* 任务 */ });
t.start();
t.join(); // 当前线程在这里卡住,直到 t 运行结束
当前线程阻塞,直到目标线程 t 执行完毕(终止)。
它底层其实也是利用了 wait() 机制,因此会释放锁。如果在 synchronized 块中调用 join(),当前线程会释放锁,允许其他线程进入临界区,直到目标线程结束或被中断。
常用于:主线程需要等待子线程计算结果后才能继续(例如:先下载数据,再处理数据)。
join在main中就是主线程等待引用join的线程运行结束,
如果是在t2中使用t1.join就是t2等待t1结束
上面使用的join都是没有参数的版本,没有参数就是,等待的线程不结束就会一直等,这个等待会很容易出bug就像租界女友的男主(忘了叫什么了),一直舔着水原千鹤,但她是租界女友而且也不会喜欢他,这时候bug就出现了,男主就算等到老死也等不到水原千鹤喜欢他(看一部劝退)
这个时候我们就该引入带参数的join了

public class demo4 {
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(()->{
System.out.println("Hello from thread");
});
Thread thread2=new Thread(()->{
System.out.println("Hello from thread");
try {
thread1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread1.start();
thread1.join();
thread2.start();
}
}
public class demo4 {
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(()->{
System.out.println("Hello from thread");
});
Thread thread2=new Thread(()->{
System.out.println("Hello from thread");
try {
thread1.join(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread1.start();
thread1.join(1000);
thread2.start();
}
}
第三个没怎么用过,我这边就不提了。
public static Thread currentThread();
咱们之前提到过这个方法,用来获取当前线程的引用
public class demo5 {
public static void main(String[] args) {
Thread t=Thread.currentThread();
System.out.println(t.getName());
}
}
再提一嘴sleep()方法:
sleep会让调用的线程阻塞等待,是有一定的时间的
线程执行sleep的时候就会使这个线程不参与cpu的调度,从而把cpu的资源让出来,给别人用。
这种操作被称为"放权",再某些开发的过程中发现cpu占用过高就可以通过sleep来进行改善。
线程状态
线程共有六种状态
- new
- runnable
- blocked
- waiting
- timed_waiting
- terminated
进程状态:
- 就绪
- 阻塞
就绪:正在cpu上执行或者随时可以去cpu上执行
阻塞:暂时不参与cpu执行
线程内的状态是一个枚举类型Thread.State
public class demo6 {
public static void main(String[] args) {
for (Thread.State state:Thread.State.values()){
System.out.println(state);
}
}
}
运行结果为:
NEW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED
new:当前Thread对象虽然有了,但是内核的线程还没有(这个时候并没有调用start)
runnable:就绪态,正在cpu上执行或者随时可以去cpu上执行
以下三个是阻塞态:
- blocked,因为锁竞争而近期的阻塞
- waiting,没有超时等待
- timed_waiting,有超时等待或者join带参数版本