源码
public final void wait() throws InterruptedException {
wait(0);
}
Object.wait(long)要跟Object.notify()/notifyAll()搭配使用。
wait 与 notify/notifyAll 方法必须在synchronized 同步代码块中使用,即要先对调用对象加锁,不放在synchronized 中则会在program runtime时扔出“java.lang.IllegalMonitorStateException”异常。
如果指定了wait的时间,到时间会自动唤醒;否则就需要 notify / notifyAll 去唤醒。
yield()方法作用是放弃当前CPU资源,让给其他线程去使用,但是放弃时间不确定。
Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁,由运行状态变为就绪状态, 但yield()从未导致线程转到等待/睡眠/阻塞状态,让OS再次选择线程。
作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
thread.join()/thread.join(long millis),假如 当前线程里调用其它线程 t 的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
join的核心源码是这个:
if (millis == 0) {
while (isAlive()) {
wait(0);
}
}
就是说调用join方法的线程,其他线程为其让步,让它先执行完才能执行,阻塞主线程,直到被唤醒。
demo:
public class ThreadMethodTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread1 = new MyThread("Thread 1");
MyThread myThread2 = new MyThread("Thread 2");
myThread1.start();
myThread1.join();
myThread2.start();
myThread2.join();
System.out.println(Thread.currentThread().getName()+ " 线程正在运行");
}
static class MyThread extends Thread{
MyThread(String name){
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程正在运行");
}
}
}
输出:
Thread 1 线程正在运行
Thread 2 线程正在运行
main 线程正在运行
myThread1.start() 后 再 myThread1.join() 这样的话,myThread1执行完才会执行 myThread2.start()。
如果改成:
public class ThreadMethodTest {
public static void main(String[] args) throws InterruptedException {
MyThread myThread1 = new MyThread("Thread 1");
MyThread myThread2 = new MyThread("Thread 2");
myThread1.start();
myThread2.start();
myThread2.join();
myThread1.join();
System.out.println(Thread.currentThread().getName()+ " 线程正在运行");
}
static class MyThread extends Thread{
MyThread(String name){
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程正在运行");
}
}
}
输出:
Thread 1 线程正在运行
Thread 2 线程正在运行
main 线程正在运行
main 线程必须等待,但是 Thread 1、Thread 2 都已经 start()了,调用join的先后顺序不影响,都是并行的。
Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,释放CPU,millis后线程自动苏醒进入就绪状态。
作用:给其它线程执行机会的最佳方式。
有时候会使用sleep(0)使CPU重新发起一次调度。
wait()和sleep()都可以通过interrupt()方法立即 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。
wait() 是Object类 的方法,sleep()是 Thread 类的方法。
wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。
都可以指定阻塞指定秒数,并返回。
sleep方法没有释放锁,而wait方法释放了锁。
使用多线程的时候,如果有一个子线程没有结束,jvm进程就不会退出,为了确保所有线程都能够结束,就引入了守护线程。
用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。
守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。
demo:
public class DaemonDemo {
public static void main(String[] args) {
Thread daemonThread1 = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
System.out.println(Thread.currentThread().getName() + " 子线程执行中....");
Thread.sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " finally 语句块");
}
}
}
}, "daemonThread1");
Thread daemonThread2 = new Thread(new Runnable() {
int i = 2;
@Override
public void run() {
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " 子线程执行中....");
}
}
}, "daemonThread2");
daemonThread1.setDaemon(true); //daemon要先于start前执行,否则会报 IllegalThreadStateException
daemonThread1.start();
daemonThread2.start();
try {
Thread.sleep(2 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("daemonThread1 状态"+daemonThread1.isAlive());
System.out.println("daemonThread2 状态"+daemonThread2.isAlive());
}
}
输出:
daemonThread1 子线程执行中....
daemonThread2 子线程执行中....
daemonThread2 子线程执行中....
daemonThread1 finally 语句块
daemonThread1 子线程执行中....
daemonThread1 finally 语句块
daemonThread1 子线程执行中....
daemonThread1 状态true
daemonThread2 状态false
上述有2个子线程daemonThread1、daemonThread2,我给daemonThread1设置为守护线程,一直是无限循环,当两个线程执行的时候,随着daemonThread2执行结束、还有main线程结束,之后显示daemonThread1还是alive的,但是因为daemonThread1设置为守护线程,有且只有守护线程的时候,jvm退出。
结论:
当线程只剩下守护线程的时候,JVM就会退出;补充一点如果还有其他的任意一个用户线程还在,JVM就不会退出。.
守护线程结束的时候,不会执行finally的语句块。
setDaemon要先于start()前执行,否则会报 IllegalThreadStateException。
参考:
https://blog.csdn.net/pange1991/article/details/53860651
https://www.cnblogs.com/baizhanshi/p/8289202.html