java如何实现“自旋”(spin)

我本以为Thread.yield()是实现自旋的,不过yield()会放弃cpu时间片,那应该也会进行上下文切换吧?那就不是自旋了吧?(我理解的自旋是:当线程需要某个资源,但这个资源没有到位,这时就进行一个死循环,从而不放弃cpu执行时间,也不进行上下文切换)

那,我该怎么实现自旋呢?

阅读 10.8k
评论
    2 个回答
    • 508

    一个简单的while就可以满足你的要求。

    目前的JVM实现自旋会消耗CPU,如果长时间不调用doNotify方法,doWait方法会一直自旋,CPU会消耗太大。

    public class MyWaitNotify3{
    
      MonitorObject myMonitorObject = new MonitorObject();
      boolean wasSignalled = false;
    
      public void doWait(){
        synchronized(myMonitorObject){
          while(!wasSignalled){
            try{
              myMonitorObject.wait();
             } catch(InterruptedException e){...}
          }
          //clear signal and continue running.
          wasSignalled = false;
        }
      }
    
      public void doNotify(){
        synchronized(myMonitorObject){
          wasSignalled = true;
          myMonitorObject.notify();
        }
      }
    }
    

    请参考 这里, 假唤醒部分。

      个人觉得wait/notify是条件变量的一种,并不是操作系统意义上的自旋,自旋应该是一个空loop,当loop的条件被其他线程改变时再进入临界区,参考java锁的种类以及辨析(一):自旋锁,其中如果觉得空循环耗CPU,方法体里加上Thread.yield()。譬如以下是一个顺序打印ABC的程序:

      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class CharacterPrint implements Runnable {
      
          private int state = 0;
          private final int COUNT = 10;
      
          private String[] chs = { "A", "B", "C", "D" };
          private final int N = chs.length;
      
          private Lock lock = new ReentrantLock();
      
          private CharacterPrint() {
          }
      
          @Override
          public void run() {
              for (int i = 0; i < N; i++) {
                  createThread(i);
              }
          }
      
          private void createThread(final int i) {
              new Thread(new Runnable() {
                  @Override
                  public void run() {
                      try {
                          while (state != i) {
                              Thread.yield();
                          }
                          ;
                          lock.lock();
                          for (int j = 0; j < COUNT; j++) {
                              System.out.println(chs[i]);
                          }
                          state = (i + 1) % N;
                      } finally {
                          lock.unlock();
                      }
                  }
              }, "thread" + i).start();
          }
      
          public static void main(String[] args) {
              new CharacterPrint().run();
          }
      }
      

      最后想说,一直都不太清楚自旋锁和条件变量差在哪里,但在posix的规范里它们就是不同的东西,参考同步和互斥的POSXI支持(互斥锁,条件变量,自旋锁)

        撰写回答

        登录后参与交流、获取后续更新提醒