【问题标题】:Java Threads Busy WaitingJava 线程忙等待
【发布时间】:2015-07-04 11:50:49
【问题描述】:

您好,我正在做一个项目,但我遇到了一个非常困难的部分。我试图寻找方法来学习如何为繁忙的等待编写 while 循环,但我没有找到任何东西,我的代码只是作为无限循环运行。有人可以帮我解释一下繁忙的等待循环应该如何工作并帮助我摆脱这个无限循环吗?

该项目希望发生以下情况:早上,学生醒来后(需要随机时间),他会去洗手间为新的一天做准备。如果卫生间已经被占用,学生会休息一下(使用 yield()),稍后他将等待(使用忙等待)等待卫生间可用。学生将按照先到先得的原则使用卫生间(您可以使用布尔数组/向量让它们按顺序释放)。

 public class Student implements Runnable 
    {
        private Random rn = new Random();
        private String threadNum;
        private volatile boolean bathroomFull = false;
        private static long time = System.currentTimeMillis();
        private Thread t;


    public Student(String studentID) 
    {
      threadNum = studentID;

      t = new Thread(this, "Student Thread #"+threadNum);
      System.out.println("thread created = " + t);
      // this will call run() function
      t.start();
   }

   public void run() 
   {
       int waitTime = rn.nextInt(4000 - 2000 + 1)+2000;

        System.out.println( "the current time is " + (System.currentTimeMillis() - time) + "and the wait time is: " +waitTime );

         //Student wakes up after random time
        while((System.currentTimeMillis()-time) < waitTime)
       {
          // System.out.println("the remaining sleep time is " + (System.currentTimeMillis()-time));
            ;
       }

      int a = rn.nextInt(4000 - 2000 + 1)+2000;
      try 
      {
          //System.out.println("I'm going to sleep for " +a + " milliseconds");
        Thread.sleep(a);
      } 
      catch (InterruptedException e) 
      {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }

      //this is the busy wait loop where is the bathroom is full then a thread will yield until it is available
    int l = rn.nextInt(10 - 1)+1;
  bathroomFull = true;

      while(bathroomFull)
        {
          for(int j = 0; j < l; j++)
          {
              System.out.println("i am in the bathroom for " + l + "minutes " + Thread.currentThread());
          }
          Thread.yield();
          bathroomFull = false;
          //exitBathroom();

        }
    bathroomFull = true;

这是我的主要方法,它允许用户指定他们想要多少个学生线程。是的,我不明白如何实现值的更改,以便可以打破繁忙的等待循环。

 public static void main(String args[]) 
   {
       int numberOfStudents;
       numberOfStudents = Integer.parseInt(JOptionPane.showInputDialog("How many students are there in the university? "));
      // System.out.println("there are " + numberOfStudents);

       for(int i = 0; i < numberOfStudents; i++)
       {   
           new Student(String.valueOf(i+1));
       }
          new Teacher();
   }

【问题讨论】:

  • 哪个循环是无限的,哪个值没有被改变,这将结束循环。 AFAICS bathroomFull 总是错误的。
  • 您的 Runnable 应该做的就是打印 the current time is ... 并在几秒钟后退出。你期待做其他事情吗?
  • 他的循环呢?你预计会发生什么?
  • @pathfinderelite 我怀疑它正在等待某人更改代码以便bathroomFull = true 某处。 ;)
  • 请不要在 cmets 中发布代码,因为它会丢失格式使其无法读取。相反,请通过editing your question 将任何新代码发布到原始问题的底部。

标签: java multithreading busy-waiting


【解决方案1】:

这是一个忙碌等待的工作示例。它使用 AtomicBoolean 来指示浴室是否被占用。原子操作是一步执行的,这对于保证线程安全很重要。我们也可以使用普通的布尔值并自己写compareAndSet

private static synchronized boolean compareAndSet(boolean expected, boolean value) {
    if (occupied == expected) { // (1)
        occupied = value; // (2)
        return true;
    } else {
        return false;
    }
}

这是 Java 实现的等价物(对于本示例)。 synchronized 是必需的,否则有可能两个线程在 (1) 执行 (2) 之前通过测试(因为这两个操作不是原子)然后两个人会进入一起上厕所……

import java.util.concurrent.atomic.AtomicBoolean;

public class Student extends Thread {

    // note the static: there is only one bathroom for all students
    private static AtomicBoolean occupied = new AtomicBoolean(false);

    private String name;

    public Student(String name) {
        this.name = name;
    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            System.out.println(name + " wet his/her pants");
        }
    }

    @Override
    public void run() {
        int r = (int)(Math.random() * 5000);
        System.out.println(name + " sleeps for " + r + " ms");
        sleep(r);
        System.out.println(name + " goes to bathroom");
        // ***** busy wait *****
        while (!occupied.compareAndSet(false, true)) {
            System.out.println(name + " takes a break");
            Thread.yield();
            sleep(1000);
        }
        // ***** end (in bathroom) *****
        System.out.println(name + " is in the bathroom");
        sleep(1000);
        occupied.set(false);
        System.out.println(name + " goes to university");
    }

    public static void main(String[] args) {
        new Student("Bob").start();
        new Student("Alice").start();
        new Student("Peter").start();
        new Student("Marcia").start();
        new Student("Desmond").start();
        new Student("Sophia").start();
    }

}

可能的输出:

Bob 睡眠了 2128 毫秒
玛西娅睡了 3357 毫秒
爱丽丝睡了 1289 毫秒
彼得睡了 820 毫秒
戴斯蒙德睡了 1878 毫秒
索菲亚睡眠时间为 2274 毫秒
彼得去洗手间
彼得在浴室里
爱丽丝去洗手间
爱丽丝休息一下
彼得上大学
戴斯蒙德去洗手间
戴斯蒙德在浴室里
鲍勃去洗手间
鲍勃休息一下
索菲亚去洗手间
索菲亚休息
爱丽丝休息一下
戴斯蒙德上大学
鲍勃在浴室里
索菲亚休息
爱丽丝休息一下
玛西娅去洗手间
玛西娅休息
鲍勃上大学
索菲亚在浴室里
爱丽丝休息一下
玛西娅休息
索菲亚上大学
爱丽丝在浴室里
玛西娅休息
爱丽丝上大学
玛西娅在浴室里
玛西娅上大学

【讨论】:

  • 谢谢,您的 while 循环比我的要好得多,谢谢您提供有关原子布尔值的信息,我以前从未听说过。我打算只使用一个向量,因为教授建议使用一个。
猜你喜欢
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 2022-11-17
  • 2011-06-09
  • 1970-01-01
相关资源
最近更新 更多