Java线程中yield与join方法的区别

长期以来,多线程问题颇为受到面试官的青睐。虽然我我的认为咱们当中不多有人能真正得到机会开发复杂的多线程应用(在过去的七年中,我获得了一个机会),可是理解多线程对增长你的信心颇有用。以前,我讨论了一个wait()和sleep()方法区别的问题,这一次,我将会讨论join()和yield()方法的区别。坦白的说,实际上我并无用过其中任何一个方法,因此,若是你感受有不恰当的地方,请提出讨论。java

Java线程调度的一点背景

在各类各样的线程中,Java虚拟机必须实现一个有优先权的、基于优先级的调度程序。这意味着Java程序中的每个线程被分配到必定的优先权,使用定义好的范围内的一个正整数表示。优先级能够被开发者改变。即便线程已经运行了必定时间,Java虚拟机也不会改变其优先级面试

优先级的值很重要,由于Java虚拟机和下层的操做系统之间的约定是操做系统必须选择有最高优先权的Java线程运行。因此咱们说Java实现了一个基于优先权的调度程序。该调度程序使用一种有优先权的方式实现,这意味着当一个有更高优先权的线程到来时,不管低优先级的线程是否在运行,都会中断(抢占)它。这个约定对于操做系统来讲并不老是这样,这意味着操做系统有时可能会选择运行一个更低优先级的线程。(我憎恨多线程的这一点,由于这不能保证任何事情)多线程

注意Java并不限定线程是以时间片运行,可是大多数操做系统却有这样的要求。在术语中常常引发混淆:抢占常常与时间片混淆。事实上,抢占意味着只有拥有高优先级的线程能够优先于低优先级的线程执行,可是当线程拥有相同优先级的时候,他们不能相互抢占。它们一般受时间片管制,但这并非Java的要求。函数

理解线程的优先权

接下来,理解线程优先级是多线程学习很重要的一步,尤为是了解yield()函数的工做过程。学习

  1. 记住当线程的优先级没有指定时,全部线程都携带普通优先级。
  2. 优先级能够用从1到10的范围指定。10表示最高优先级,1表示最低优先级,5是普通优先级。
  3. 记住优先级最高的线程在执行时被给予优先。可是不能保证线程在启动时就进入运行状态。
  4. 与在线程池中等待运行机会的线程相比,当前正在运行的线程可能老是拥有更高的优先级。
  5. 由调度程序决定哪个线程被执行。
  6. t.setPriority()用来设定线程的优先级。
  7. 记住在线程开始方法被调用以前,线程的优先级应该被设定。
  8. 你可使用常量,如MIN_PRIORITY,MAX_PRIORITY,NORM_PRIORITY来设定优先级

如今,当咱们对线程调度和线程优先级有必定理解后,让咱们进入主题。this

yield()方法

理论上,yield意味着放手,放弃,投降。一个调用yield()方法的线程告诉虚拟机它乐意让其余线程占用本身的位置。这代表该线程没有在作一些紧急的事情。注意,这仅是一个暗示,并不能保证不会产生任何影响。spa

在Thread.java中yield()定义以下:操作系统

1
2
3
4
5
6
7
/**
   * A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore
   * this hint. Yield is a heuristic attempt to improve relative progression between threads that would otherwise over-utilize a CPU.
   * Its use should be combined with detailed profiling and benchmarking to ensure that it actually has the desired effect.
   */
 
public static native void yield();

让咱们列举一下关于以上定义重要的几点:线程

  • Yield是一个静态的原生(native)方法
  • Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
  • Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
  • 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态

yield()方法使用示例

在下面的示例程序中,我随意的建立了名为生产者和消费者的两个线程。生产者设定为最小优先级,消费者设定为最高优先级。在Thread.yield()注释和非注释的状况下我将分别运行该程序。没有调用yield()方法时,虽然输出有时改变,可是一般消费者行先打印出来,而后事生产者。code

调用yield()方法时,两个线程依次打印,而后将执行机会交给对方,一直这样进行下去。

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
35
36
37
38
39
40
package test.core.threads;
 
public class YieldExample
{
    public static void main(String[] args)
    {
       Thread producer = new Producer();
       Thread consumer = new Consumer();
 
       producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
       consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
 
       producer.start();
       consumer.start();
    }
}
 
class Producer extends Thread
{
    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
{
    public void run()
    {
       for ( int i = 0 ; i < 5 ; i++)
       {
          System.out.println( "I am Consumer : Consumed Item " + i);
          Thread.yield();
       }
    }
}

上述程序在没有调用yield()方法状况下的输出:

1
2
3
4
5
6
7
8
9
10
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

上述程序在调用yield()方法状况下的输出:

1
2
3
4
5
6
7
8
9
10
I am Producer : Produced Item 0
  I am Consumer : Consumed Item 0
  I am Producer : Produced Item 1
  I am Consumer : Consumed Item 1
  I am Producer : Produced Item 2
  I am Consumer : Consumed Item 2
  I am Producer : Produced Item 3
  I am Consumer : Consumed Item 3
  I am Producer : Produced Item 4
  I am Consumer : Consumed Item 4

join()方法

线程实例的方法join()方法可使得一个线程在另外一个线程结束后再执行。若是join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。

1
2
3
//Waits for this thread to die.
 
public final void join() throws InterruptedException

在join()方法内设定超时,使得join()方法的影响在特定超时后无效。当超时时,主方法和任务线程申请运行的时候是平等的。然而,当涉及sleep时,join()方法依靠操做系统计时,因此你不该该假定join()方法将会等待你指定的时间。

像sleep,join经过抛出InterruptedException对中断作出回应。

join()方法使用示例

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
35
36
37
38
39
40
41
package test.core.threads;
 
public class JoinExample
{
    public static void main(String[] args) throws InterruptedException
    {
       Thread t = new Thread( new Runnable()
          {
             public void run()
             {
                System.out.println( "First task started" );
                System.out.println( "Sleeping for 2 seconds" );
                try
                {
                   Thread.sleep( 2000 );
                } catch (InterruptedException e)
                {
                   e.printStackTrace();
                }
                System.out.println( "First task completed" );
             }
          });
       Thread t1 = new Thread( new Runnable()
          {
             public void run()
             {
                System.out.println( "Second task completed" );
             }
          });
       t.start(); // Line 15
       t.join(); // Line 16
       t1.start();
    }
}
 
Output:
 
First task started
Sleeping for 2 seconds
First task completed
Second task completed

这是一些很小却很重要的概念。在评论部分让我知道你的想法。

相关文章
相关标签/搜索