【问题标题】:How does method yield work?方法产量如何工作?
【发布时间】:2011-07-06 15:10:31
【问题描述】:

在javadoc中有说yield方法

使当前正在执行的线程对象暂时暂停并允许其他线程执行。

Katherine Sierra 和 Bert Bates SCJP 的书说

yield() 应该做的是 使当前正在运行的线程头回到可运行状态以允许其他线程 轮到他们的优先级相同。

那么实际上方法在做什么呢?

【问题讨论】:

  • 我在这里没有看到问题。您对引用的文档有什么不理解的地方?
  • 我只是认为相同优先级的线程不包括优先级高于1的线程,产生所谓的方法
  • 源码说明一切public static native void yield();:p
  • 因为我确定它在 Windows 上的作用 - 调用 sleep(0): msdn.microsoft.com/en-us/library/ms686298%28v=vs.85%29.aspx IOW: 将线程返回到 OS 调度程序队列。
  • 如果你把 yield() 放在一个繁忙的循环中,它平均需要大约 20 微秒,并且在 99%+ 的时间里它不会传递给另一个线程。一个进程可以随时传递给另一个线程,它只会让它更有可能发生在你调用 yield() 的地方。

标签: java multithreading yield


【解决方案1】:

给定一个多线程应用程序,yield 将导致当前正在执行的线程暂停执行并设置为等待状态。然后 JVM 将开始运行之前处于等待状态的另一个线程。

我相信刚刚产生的同一个线程在技术上可以重新启动。

不过,我还没有在野外看到过这种情况。所以我认为避免是安全的。

详细说明:

在多线程环境中,线程根据 JVM 的意愿被调度和不调度。因此,即使没有在代码中调用 yield,当 JVM 决定应该时,您的线程也可以/将自动让步给其他线程。这允许多线程在只有一个处理核心的环境中工作。

调用 yield 只是告诉 JVM 将当前线程置于等待状态,即使 JVM 不打算这样做。

我将尝试一个插图:
以下是2个线程随时间执行的非常简化的说明(假设1个核心)-

Thread\Time    1    2    3    4    5    6    7    8    9
Thread 1    -----------       -----          -------
Thread 2               -------     ----------       ------

每当您看到'-' 时,这意味着线程正在执行。 ' ' 表示线程正在等待。如您所见,一次实际上只能运行 1 个线程。因此,当 1 运行时,另一个等待。 yield 的目的是让其他线程有机会在当前运行的线程之前运行。

【讨论】:

  • +1 我一直觉得将yield() 视为对调度程序的提示非常有用。 (我知道从技术上讲这不是一个提示,但在很多方面它的行为就像一个提示。)
  • 根据 Java 规范,yield() 可能是空操作。如果你想强制让步,你可能想试试Thread.sleep(1)。但是,只有在极少数情况下才需要这样使用。
  • @Enno,您是否确认这一行-“我相信刚刚产生的同一个线程在技术上可以安排重新启动。”?
  • @jjnguy:不一定.. 规范说“yield 操作需要 [not to] 具有可观察的效果”,所以如果我要实现一个 JVM,我会简单地忽略(例如在编译时删除)任何对yield的调用,对吗?但不确定实际的 JVM 是如何处理产量的。
  • @Enno,啊,我明白你的意思了。哈,这可能也是我会做的。
【解决方案2】:

线程可能处于就绪(可运行)、阻塞(例如,等待某个 io 完成)或运行状态;这对所有线程实现都是通用的,尽管某些特定的实现可能有更多的状态。

Yield 导致线程从运行变为可运行,并等待调度程序将其更改为将来再次运行。这就是 SCJP 书中的意思。

对于线程来说,它似乎已经暂停了一段时间,就像 javadoc 中描述的那样。所以这两种说法都是正确的,只是措辞不同。

【讨论】:

    【解决方案3】:

    yield() 通常用于当您在线程上等待某事发生但又不想用 while(condition){ ...} 之类的东西阻塞 CPC 周期时使用。 yield() 的工作方式因平台而异,并且取决于线程调度程序,您不应依赖它以特定方式运行。

    【讨论】:

      【解决方案4】:

      它起源于协同多任务的时代。 基本思想是,处理器只执行一个线程,直到:

      1. 本帖结束
      2. 此线程执行一些阻塞操作,如object.wait()Thread.sleep,等待一些 IO 操作完成,等待一些对象监视器,或类似的。
      3. 此线程调用Thread.yield()

      在每种情况下,线程调度程序都会选择另一个线程来执行。因此,为了对其他线程公平,您将在没有任何阻塞操作的较长循环中定期调用yield()。 (如果没有其他线程准备好运行,那么相同的线程将再次被调度,所以不会有很大的性能损失。)

      在现代虚拟机中,线程切换可以发生在任何一点,不仅列出这些,线程甚至可以同时执行,所以这并不是真正必要的,有些虚拟机可能会完全忽略它(类似于System.gc()。)

      【讨论】:

      • 你已经省略了任何阻塞 IO 和 LockSupport.park,线程可以在其中跳出
      • @bestsss:提到阻塞 IO(“等待某些 IO 操作完成”),LockSupport.park 包含在“或类似”中。但我认为当需要 yield() 时,这种方法(和类)还不存在。
      • ŭlo,我的错,IO 已经在我脑海中溜走。 LockSupport 是在 1.5 中引入的(该方法有效地调用了Unsafe.park),Thread.yeild 自 Java 诞生以来就已经可用。
      【解决方案5】:

      yield() 方法用于确保应用程序中所有相同优先级的线程不会导致starvation。例如一个应用程序中有五个线程,它们都具有相同的优先级。现在假设一个线程有机会运行并且该线程需要很长时间才能完成其任务,因此其他线程将没有机会运行。因此,为了避免这种情况,yield() 可以进行救援。

      【讨论】:

        【解决方案6】:

        最终,对yield() 的调用会导致调用类似这样的 os 方法,原则上这会将任务本身放回运行队列并让下一个任务运行 (source):

         /**
          * sys_sched_yield - yield the current processor to other threads.
          *
          * This function yields the current CPU to other tasks. If there are no
          * other threads running on this CPU then this function will return.
          */
         SYSCALL_DEFINE0(sched_yield)
         {
                /*
                 *  lock this runqueue and disable interrupts.
                 */
                 struct rq *rq = this_rq_lock();
        
                 schedstat_inc(rq, yld_count);
                 current->sched_class->yield_task(rq);
        
                 /*
                  * Since we are going to call schedule() anyway, there's
                  * no need to preempt or enable interrupts:
                  */
                 __release(rq->lock);
                 spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
                 _raw_spin_unlock(&rq->lock);
                 preempt_enable_no_resched();
        
                 schedule();
        
                 return 0;
         }
        

        【讨论】:

          【解决方案7】:

          希望对你有帮助!

          package yield;
          
          public class ThreadYieldApp {
          
              Thread th1 = new Thread("Thread 1") {
                  public void run() {
          
                      for(int i = 0; i <= 10; i++) {
                          System.out.println("Before Yield - " + Thread.currentThread().getName() + " at index - " + i);
                          //Currently pauses the thread and checks for other threads of same priority, and passes control to it based on Thread Scheduler pick 
                          Thread.yield();
                          System.out.println("Currently running - " + Thread.currentThread().getName() + " at index - " + i);
                      }
                  }
              };
          
              Thread th2 = new Thread("Thread 2") {
                  public void run() {
                      for(int i = 0; i <= 10; i++) {
                          System.out.println("Currently running - " + Thread.currentThread().getName() + " at index - " + i);
                      }
                  }
              };
          
          
              public static void main(String[] args) {
                  ThreadYieldApp threadYieldApp = new ThreadYieldApp();
                  threadYieldApp.th1.start();
                  threadYieldApp.th2.start();
              }
          
              //Happy coding -- Parthasarathy S
          }
          

          【讨论】:

            猜你喜欢
            • 2018-10-16
            • 2013-01-29
            • 2013-04-23
            • 1970-01-01
            • 2019-04-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多