sleep()和yield()以及join()

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

猜你喜欢

转载自blog.csdn.net/qq_32924343/article/details/79929661