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