1

I am creating a simulator, but for simplicity sake, it's a 'game'. Thus it has a render() and an update() function. I've been trying to practice lambda expressions during summer but I cannot seem to be able to wrap my head around doing a lambda expression of another lambda expression. I am probably saying this wrong, but what I am trying to do is start two threads, one that loops rendering, and another that loops updating. I can get this far:

void render() {
    //draw entities, etc.
}
void update() {
    //update player/enemies, etc.
}

public GameFrame() {
    /* init stuff */
    Thread updateThread = new Thread(this::update);
    Thread renderThread = new Thread(this::render);
}

This is not what I want because this only runs update and draw once, thus this is just two threads, one that renders once, and one that updates once (no looping). I want to create a function that does something along the lines of:

public void loop(Supplier< /*?*/ > arg) {
    long startTime;
    while(running) {
        startTime = System.currentTimeMillis();
        supplier.get(arg) // <- not sure about this either
        try {
            long sleepTime = 1000/FPS - (System.currentTimeMillis() - startTime);
            if(sleepTime > 0)
                Thread.sleep(sleepTime);
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Then I would call the function like this?

loop(this::update);

I believe this would cause the supplied function to loop, thus I tried:

Thread updateThread = new Thread(this::loop(this::update));

Or even something like this:

new Thread(() -> loop(this::update)).start();

I know I can just make my render function while loop, and my update function while loop by just copying the 'void loop(Supplier arg)' code into each part, but I wanted to see if I could do it this way anyways.

I don't need an exact answer, I would just like some guidance to what I'm not thinking about/what I'm thinking about wrong. I haven't been able to make much progress by reading up on lambda expressions on oracle.

5
  • 3
    Your last idea is OK, but the loop() method should take a Runnable as argument, not a Supplier, since you want to execute a function that takes no argument, and returns nothing, and that's what a Runnable is. And it should thus call run() on that runnable, at each iteration. Commented Aug 17, 2016 at 6:24
  • 4
    First thing, why are you thinking about a "sleep loop" instead of using a Timer? Commented Aug 17, 2016 at 6:25
  • 2
    As an alternative to using bare metal threads, you could as well use ExecutorServices and keep submitting jobs into them. That wont help with lambdas, but it will making testing a whole lot easier (because you can replace a multi-threaded executor service with a "do-it-in-the-current-thread" executor easily. Commented Aug 17, 2016 at 6:33
  • @RealSkeptic I will look into timers. I didn't know what they were though. Commented Aug 17, 2016 at 6:46
  • @JBNizet Ah! Thank you that makes so much sense! I always looped inside the run() but never looped outside of it so I was not used to the idea. Commented Aug 17, 2016 at 6:50

1 Answer 1

1

The Thread constructor takes Runnable and this is what you want to execute repeatedly. Therefore you don't need Supplier as parameter type for loop, but Runnable.

public void loop(Runnable arg) {
    long startTime;
    while(running) {
        startTime = System.currentTimeMillis();
        arg.run();
        try {
            long sleepTime = 1000/FPS - (System.currentTimeMillis() - startTime);
            if(sleepTime > 0)
                Thread.sleep(sleepTime);
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Note that is isn't possible to specify parameters with a method reference. This is invalid syntax:

this::loop(this::update)

The statement you want the thread to execute is:

loop(this::update);

so you need to use a lambda expression with this statement as body as parameter of the Thread constructor:

Thread updateThread = new Thread(() -> loop(this::update));

Note that a ScheduledExecutorService provides the kind of scheduling you're implementing here.

Sign up to request clarification or add additional context in comments.

2 Comments

would you happen to know why Java does not allow: this::loop(this::update)? I noticed that if a function has a parameter, I must reference it like () -> doFoo(arg). Why is this?
a) you need a target type for a method reference to work (for this::loop there isn't one) and b) even if you had a target type java doesn't support currying as a language feature, so you need to implement it differently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.