Java謎題3:汽車 - 解決方案

jdon發表於2019-09-18

讓我們看看如何讓升級後的汽車打破速度限制。

package car;
  
public final class Car {
    private final int MAX_SPEED = 100;
  
    private int speed = 0;
  
    public synchronized void accelerate(int acceleration) {
        speed += acceleration;
        if (speed > MAX_SPEED)
            crash();
    }
  
    public synchronized void crash() {
        speed = 0;
    }
  
    public synchronized void vroom() {
        if (speed > MAX_SPEED * 10) {
            // The goal is to reach this line
            System.out.println("Vroom!");
        }
    }
}


給所有賽車手的教訓是:我們希望賽車加速很多(speed += acceleration必須執行),但不要在撞到樹並完全停止的地方執行(speed = 0不得執行)。

這兩種陳述之間會發生什麼?if(speed>max_speed)對我們沒有任何幫助,因為speed確實需要高於max_speed。所以剩下的就是對crash()方法的呼叫。那一個必須失敗:我們想要crash()崩潰!

我們可以透過確保堆疊上沒有足夠的空間來呼叫任何方法來實現。如果呼叫accelerate時堆疊幾乎已滿,則呼叫crash()時會出現stackOverflowerRor。我們怎麼才能讓這堆東西都快滿了呢?一種方法是強制它:一種無限遞迴的方法,不斷嘗試。

package driver;
 
import car.Car;
 
public class Driver {
    private static Car car = new Car();
     
    public static void main(String args[]) {
        try {
            recurse();
        } catch (StackOverflowError e) {
        }
         
        car.vroom();
    }
     
    public static void recurse() {
        car.accelerate(1001);
        recurse();
    }
}


起效了!…至少在某些系統上。這裡的一個問題是,如果你試圖加速這麼多並繼續崩潰(在我的系統崩潰1500次之後),jvm會做一些汽車製造商從未做過的事情:它最佳化汽車,以確保它能夠很快崩潰。它把對crash()的呼叫排成一行,將其本質上減少為if(speed>max_speed)speed=0;。這樣就消除了stackoverflower錯誤的可能性。

我們可以透過稍微溫和些來解決這個問題:少些崩潰,因此JVM不會決定最佳化它。首先增加堆疊直到它接近需要的位置,然後才將汽車新增到程式中。但知道你接近極限的唯一方法就是擊中它。因此,我們在沒有汽車的情況下遞迴,直到我們遇到堆疊溢位,然後向後退幾步,再應用上面的強制解決方案:

package driver;
 
import car.Car;
 
public class Driver {
    static Car car = new Car();
    static int a = 0;
 
    public static void main(String args[]) {
        recurse();
        car.vroom();
    }
 
    static void recurse() {
        try {
            recurse(); // recurse without the car
        } catch (StackOverflowError e) {
            // when we've hit the limit of the stack, just go back out
        }
        if (a++ == 10) { // after taking 10 steps back
            recurse2(); // recurse with the car
        }
    }
 
    static void recurse2() {
        car.accelerate(1001);
        recurse2();
    }
}


另一種解決方案是使用Thread.stop。但是很難把時間計算得恰到好處; 它在整個系統中不可靠。

相關文章