yield()方法意味着“让步”。
1.让步的意思线程告诉cpu,你可以去执行其他的线程,我可以让出当前cpu的执行权利。
2.它的作用就是将当前线程从执行状态转变到可执行状态。
3.但是它不能保证其他线程一定能够执行,因为执行过yield的线程当前依然是可执行的状态,有可能被cpu再次执行。
4.但是执行yield的线程不会释放锁,这是要注意的。
例子1
public class Test { public static void main(String[] args) throws Exception{ Producer p = new Producer(); Consumer c = new Consumer(); p.setPriority(Thread.MIN_PRIORITY); //Min Priority c.setPriority(Thread.MAX_PRIORITY); //Max Priority p.start(); c.start(); } } class Producer extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Producer : Produced Item " + i); // Thread.yield(); } } } class Consumer extends Thread { @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println("I am Consumer : Consumed Item " + i); // Thread.yield(); } } }
I am Consumer : Consumed Item 0 I am Consumer : Consumed Item 1 I am Consumer : Consumed Item 2 I am Consumer : Consumed Item 3 I am Consumer : Consumed Item 4 I am Producer : Produced Item 0 I am Producer : Produced Item 1 I am Producer : Produced Item 2 I am Producer : Produced Item 3 I am Producer : Produced Item 4我执行过多次,基本上是这个输出。权限高的线程优先执行了。。
例子2
将注释打开,可以看见执行结果时变时不变。充满了不确定性。
sleep()意味着休眠
1.意味着当前线程放弃剩余时间片,进入阻塞状态。
2.其他线程有机会来竞争时间片的使用。
3.注意,sleep时并不会释放当前的锁,意味着即使当前线程在锁块内休眠,其他线程也无法执行。
可以参考之前的博客
join()意味着顺序
顺序是我自己总结出来的,如果你想让启动的线程按照顺序执行,那么就请使用join()。
public class TestA { public static void main(String[] args) throws Exception{ System.out.println("start main"); T1 t1 =new T1(); T2 t2 =new T2(t1); T3 t3 =new T3(t2); t1.start(); t2.start(); t3.start(); System.out.println("end main"); } } class T1 extends Thread { @Override public void run() { try{ Thread.sleep(1000); }catch (Exception e){ } System.out.println("t1 is sleeping"); try{ Thread.sleep(2000); }catch (Exception e){ } System.out.println("I am T1"); } } class T2 extends Thread { private Thread thread; public T2(Thread thread) { this.thread = thread; } @Override public void run() { try{ thread.join(); }catch (Exception e){ e.printStackTrace(); } System.out.println("I am T2"); } } class T3 extends Thread { private Thread thread; public T3(Thread thread) { this.thread = thread; } @Override public void run() { try{ thread.join(); }catch (Exception e){ e.printStackTrace(); } System.out.println("I am T3"); } }
start main end main t1 is sleeping I am T1 I am T2 I am T3
两个问题:
1.为什么主程序先执行完了?或者可以这么说,主程序和子程序是并行的
2.为什么join顺序执行了?
想要了解内幕,必须去知道join的底层方法做了哪些不为人知的操作。
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); } } else {//这个分支是等待固定时间,如果子没结束,那么就不等待了。。。 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
存在3个分支,常见的是使用的分支2,无限等待直到子线程结束。
内部的方法是调用的wait(0)。wait和notify一对兄弟上一篇刚好研究过wait/notify
注意如果这边使用了wait,那么必定在使用时需要synchronize的支持。上面的方法前面就加入了关键字synchronize,注意下。
因此结合wait的代码分析,流程应该如下(假设t1,t2,t3在start之后cpu是按照这个顺序调度的,这样好理解一些):
首先来分析t1,t2线程是如何规定了顺序的(假设现在t1和t2已经启动成功,不然isAlive()方法没作用)
1.t2执行了t1.join()方法,因此t2获取到了join()方法上的锁,这个锁的对象是谁?可以看见synchronize是加在方法层面上的,因此是this。那是谁调用了join()方法呢?是t1,所以t2获取的是t1这个线程对象锁。
public final synchronized void join(long millis)
2.然后t2执行wait流程,我们知道,wait的时候需要释放锁,阻塞自己。因此t2释放掉t1对象锁,然后进入等待队列_waitSet中。
3.t3和t2的流程一样。因此t3也被阻塞了,等待着t2的执行,而t2又等待着t1的执行。
4.那么大家就要问了,大家最终是怎么执行的?熟悉的朋友应该知道,既然使用了wait,那么对应的就要使用notify。见下:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) ; ... lock.notify_all(thread); ... }在线程exit的时候会进行notifyall的操作,谁需要被通知呢?可见到的是thread,也就是当前线程,在本例子中就是t1线程对象, 那么谁因为这个对象进行了wait操作的?t2线程。
后续t2和t3的时候也是同一个道理,不在分析。
问题一:为什么主线程也并行了,因为顺序关系只存在于t1,t2,t3之间,主线程最为单独的执行线程,和它们没有任何关系,因此将它们执行完start()之后,会继续执行自身的代码。
问题二:至此应该就解释了它为什么能顺序执行了吧,其本质原因就是它在顺序唤醒t2和t3,也就是它在顺序唤醒t1个t2两个线程对象锁(它们分别被t2和t3线程进行过wait操作)。
2.join(n)的问题
join(0)也就是无无限期等待,而join(n)的话则是在等待n之后就执行自己的代码,不会等待子线程结束之后主线程再执行。
public class TestA { public static void main(String[] args) throws Exception{ System.out.println("start main"); T1 t1 =new T1(); T2 t2 =new T2(t1); // T3 t3 =new T3(t2); t1.start(); t2.start(); // t3.start(); System.out.println("end main"); } } class T1 extends Thread { @Override public void run() { try{ Thread.sleep(1000); }catch (Exception e){ } System.out.println("t1 is sleeping"); try{ Thread.sleep(2000); }catch (Exception e){ } System.out.println("I am T1"); } } class T2 extends Thread { private Thread thread; public T2(Thread thread) { this.thread = thread; } @Override public void run() { try{ thread.join(1000); }catch (Exception e){ e.printStackTrace(); } System.out.println("I am T2"); } }
start main end main I am T2 t1 is sleeping I am T1
3.后续验证
我们知道在做join时,主线程必须拿到子线程的对象的锁才能执行wait操作,那么如果拿不到的话,它依然要等待。直到获取到锁。
public class TestA { public static void main(String[] args) throws Exception{ Thread t2 = new T2(); t2.start(); Thread.sleep(1000); t2.join(1000); System.out.println("main end"); } } class T2 extends Thread { @Override public void run() { synchronized (this) { System.out.println("I am T2 start"); try { Thread.sleep(5000); } catch (Exception e) { e.printStackTrace(); } System.out.println("I am T2 end"); } } }
I am T2 start //5s的停顿时间 I am T2 end main end