Java在多线程开发中,Object类的sleep、join、yield这三个方法比较常见。
sleep()的作用和用法
sleep方法可以让线程进入Timed_Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。
sleep()不会释放monitor锁。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public class SleepDontReleaseMonitor implements Runnable {
@Override public synchronized void run() { sync(); }
private void sync() { System.out.println(Thread.currentThread().getName() + "获取了锁"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "退出同步代码块"); }
public static void main(String[] args) { Runnable runnable = new SleepDontReleaseMonitor(); Thread threadA = new Thread(runnable); Thread threadB = new Thread(runnable); threadA.start(); threadB.start(); } }
|

sleep()也不会释放lock锁。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;
public class SleepDontReleaseLock implements Runnable {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) { Runnable runnable = new SleepDontReleaseLock(); Thread threadA = new Thread(runnable); Thread threadB = new Thread(runnable); threadA.start(); threadB.start(); }
@Override public void run() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "获取了锁"); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "已经苏醒"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|

sleep也可以响应中断。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.util.concurrent.TimeUnit;
public class SleepInterrupted implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { try { System.out.println(i); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } }
public static void main(String[] args) throws InterruptedException { Runnable runnable = new SleepInterrupted(); Thread thread = new Thread(runnable); thread.start(); TimeUnit.SECONDS.sleep(3); thread.interrupt(); } }
|

wait和sleep方法的相同点:
- wait和sleep方法都可以使线程阻塞,对应线程状态是Waiting或Time_Waiting。
- wait和sleep方法都可以响应中断Thread.interrupt()。
wait和sleep方法的不同点:
- wait方法的执行必须在同步方法中进行,而sleep则不需要。
- 在同步方法里执行sleep方法时,不会释放monitor锁,但是wait方法会释放monitor锁。
- sleep()方法短暂休眠之后会主动退出阻塞,而没有指定时间的wait方法则需要被其他线程中断后才能退出阻塞。
- wait()、notify()/notifyAll()是Object类的方法,sleep()是Thread类的方法。
join()的作用和用法
join()方法的作用就是:因为新的线程加入了我们,所以我们要等待它执行完再出发。比如main线程里面新建了一个线程并且调用了join()方法,那么main线程就得等待这个子线程执行完毕后才能继续执行。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
public class Join {
public static void main(String[] args) throws InterruptedException { Thread threadA = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); }); Thread threadB = new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行完毕"); }); threadA.start(); threadB.start(); System.out.println("开始等待子线程执行"); threadA.join(); threadB.join(); System.out.println("所有子线程执行完毕"); } }
|

join也可以响应中断,不过不是调用join()方法的线程响应中断而是被加入的线程才能响应中断。比如线程A加入了main线程,那么就是main线程去响应中断。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class JoinInterrupted { public static void main(String[] args) { Thread main = Thread.currentThread(); Thread threadA = new Thread(() -> { main.interrupt(); }); threadA.start(); try { threadA.join(); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "响应了中断"); e.printStackTrace(); } } }
|

yield()的作用
yield()方法会让出当前线程CPU的时间片,使正在运行中的线程重新Runnable状态,并重新竞争CPU的调度权。它可能会获取到,也有可能被其他线程获取到。yield实际用得并不多,不过在juc工具包中用得比较多。
yield和sleep方法的相同点:
- yield,sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。
yield和sleep方法的不同点:
- yield不能被中断,而sleep则可以响应中断。
- sleep可以指定具体休眠的时间,而yield则依赖CPU的时间片划分。