# 线程的 sleep、wait、join、yield 如何使用

sleep:

sleep 的作用是让目前正在执行的线程休眠,让 CPU 去执行其他的任务。从线程状态来说,就是从执行状态变成限时阻塞状态。Sleep () 方法定义在 Thread 类中,是一组静态方法,有两个重载版本:

// 使目前正在执行的线程休眠 millis 毫秒
     public static void sleep(long millis) throws InterruptException;
     
     // 使目前正在执行的线程休眠 millis 毫秒,nanos 纳秒
     public static void sleep(long millis,int nanos) throws InterruptException;

sleep () 方法会有 InterruptException 受检异常抛出,如果调用了 sleep () 方法,就必须进行异常审查,捕获 InterruptedException 异常,或者再次通过方法声明存在 InterruptedException 异常。

wait (必须先获得对应的锁才能调用): 让线程进入等待状态,释放当前线程持有的锁资源线程只有在 notify 或者 notifyAll 方法调用后才会被唤醒,然后去争夺锁.

class ThreadA extends Thread{
	public ThreadA(String name) {
		super(name);
	}
	public void run() {
		synchronized (this) {
			try {						
				Thread.sleep(1000);	//	使当前线阻塞 1s,确保主程序的 t1.wait (); 执行之后再执行 notify ()
			} catch (Exception e) {
				e.printStackTrace();
			}			
			System.out.println(Thread.currentThread().getName()+" call notify()");
			// 唤醒当前的 wait 线程
			this.notify();
		}
	}
}
public class WaitTest {
	public static void main(String[] args) {
		ThreadA t1 = new ThreadA("t1");
		synchronized(t1) {
			try {
				// 启动 “线程 t1”
				System.out.println(Thread.currentThread().getName()+" start t1");
				t1.start();
				// 主线程等待 t1 通过 notify () 唤醒。
				System.out.println(Thread.currentThread().getName()+" wait()");
				t1.wait();  //  不是使 t1 线程等待,而是当前执行 wait 的线程等待
				System.out.println(Thread.currentThread().getName()+" continue");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

输出:

main start t1
main wait()
t1 call notify()
main continue

join: 线程之间协同方式,使用场景:线程 C 必须等待线程 B 运行完毕后才可以执行,那么就可以在线程 B 的代码中加入 thread_c.join ();;

public class TestThread {
        
        public static void main(String[] args) {
            ThreadB t1 = new ThreadB("thread-b");
            t1.start();
        }
    }
	
	class ThreadB extends Thread {
        public ThreadB(String name) {
            super(name);
        }
        public void run() {
            System.out.println(Thread.currentThread().getName());
            Thread thread_c = new ThreadC("thread-c");
            try {
                thread_c.join();
                thread_c.start();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
	class ThreadC extends Thread {
        public ThreadC(String name) {
            super(name);
        }
        public void run() {
            System.out.println(Thread.currentThread().getName());
        }
    }
// 输出
//thread-b
//thread-c
         
     // 重载版本 1:此方法会把当前线程变为 TIMED_WAITING,直到被合并线程执行结束
     public final void join() throws InterruptedException:
     
     // 重载版本 2:此方法会把当前线程变为 TIMED_WAITING,直到被合并线程执行结束,或者等待被合并线程执行 millis 的时间
     public final synchronized void join(long millis) throws InterruptedException:
     
     // 重载版本 3:此方法会把当前线程变为 TIMED_WAITING,直到被合并线程执行结束,或者等待被合并线程执行 millis+nanos 的时间
     public final synchroinzed void join(long millis, int nanos) throws InterruptedException:

yield: 让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用 yield () 的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证 yield () 达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。

public class Test03 implements Runnable{
 
 
private String name;     
   
   private Test03(String name) {     
       this.name = name;     
   }  
 
    public synchronized void run() {
            System.out.println(name + " -> Start.");
            for(int i = 0; i < 100; i++) {
                if(i == 50) {
                	Thread.yield();
    			}
    			System.out.println(Thread.currentThread().getName() + "'s i=" + (i+1));
   			 }
    		System.out.println(name + "X -> End.");
    }
    public static void main(String[] args) {
            Test03 t1 = new Test03("A");
            Thread thread1 = new Thread(t1);
            Thread thread2 = new Thread(t1);
            thread1.start();
            thread2.start();
    }
结果:
A -> Start.
Thread-0's i=1
Thread-0's i=2
.........省略
Thread-0's i=99
Thread-0's i=100
AX -> End.
A -> Start.
Thread-1's i=1
Thread-1's i=2
...省略
Thread-1's i=99
Thread-1's i=100
AX -> End.

总结起来,Thread.yeid () 方法有以下特点:

(1)yield 仅能使一个线程从运行状态转到就绪状态,而不是阻塞状态。

(2)yield 不能保证使得当前正在运行的线程迅速转换到就绪状态。

(3)即使完成了迅速切换,系统通过线程调度机制从所有就绪线程中挑选下一个执行线程时,就绪的线程有可能被选中,也有可能不被选中,其调度的过程受到其他因素(如优先级)的影响。