Phobos - A JavaFX Games EnginePart 1: An Introduction to Software Design Patterns and Threading

For the past couple of months I have been required to undertake a games design project at University. One of the things that came out of this project is Phobos - an extensible engine built with JavaFX. Now, with the blessing of my course leader, I'm pleased to offer a series of tutorials which will culminate in you building your very own game engine. (that I'll actually complete for once).

Throughout this journey of code and compilation I'll introduce you to topics that were new to me at one point. Today we'll be exploring Software Design Patterns and Threading. But first, a definition of terms.

Software Design Patterns - The Singleton
A Software Design Pattern (we're going to call this DP for brevity) is a way of doing something with code. A Pattern by design. Singletons are a DP. They're a way of ensuring only one of something can exist. Put simply, a Singleton contains a circular reference to itself. I'll talk more about this later on.

Finite State Machine
A Finite State Machine is a way of managing the states of a program. Again, we're going to come back to this in a bit.

The two concepts above - Singletons and Finite State Machines - form the basis of Phobos. The very core of the engine, conveniently named Engine is a Singleton and a Finite State Machine. All of the key engine related processing - managing the state of the engine, it's screens, threads, data (or, in laymans terms, everything) - happens here. It's designed in a way that means it can be called from anywhere without instantiation and can manage everything from one place. Many engines are designed this way, though not necessarily as singletons. This design pattern is somewhat similar to MVC, another idea that we'll explore later in the series.

Without further ado, however, let's explore...

Finite States and Thread Safety
Consider this: A program can have a number of states. It can be running. It can be asleep. It can be deadlocked or involved in a race condition. It can be many things, but only ever one at a time. We call this its state. Let's extrapolate that to a game. It can be paused. It can be on a menu. It could be on a pause menu (!). These are also states. Within the scope of the program we require a way to define and track these states. To do this we must build a Finite State Manager.

First, though, we must explore the topic of threading. I looked for a decent threading tutorial but came up pretty dry. I'd recommend doing some reading on the topic but for the purposes of this I have an analogy relating to data access among threads.

My absolutely fantastic thread analogy.
Consider a person. That person is a thread. The person is assigned a task and assigned a box. That box is memory space. Now consider that there are 3 of these peoplethreads. Each person has their own box which only they can access. Now consider a bigger box that all 3 people can access. We'll call this global memory. Global Memory is volatile, meaning it will be affected by everything you can possibly imagine. What happens if 2 of the people try to change a part of the box at once? One of a few things could happen. They could both try to get to it at once - this is a race condition - or they could be super polite and keep offering it back and forth - a deadlock. Alternatively, you could have a sign telling them to form an orderly queue. This is the wonderful concept of thread synchronisation.

When you have two portions of a program attempting to access shared memory space, you have to be incredibly careful to ensure that only one thread can access it at once. In Java, we do this using the synchronized statement. There's two ways of achieving thread synchronization using this method:

Choosing which one to use is important, but a relatively easy decision:If the entire method is accessing volatile memory, mark the method as synchronized. If only a small portion of the method is, mark that block as synchronized.

In reality, creating thread-safe code is far more complicated than this and is outside of the scope of this tutorial - if it interests you I urge you to delve into some reading because it's a truly awesome topic. Alas, within the scope of this tutorial, the synchronized statement is as far as you need to go.

You might be asking why this matters at all. Singletons. That's why. If only one of them can exist then it can only exist in the global memory space we discussed above. Because of this, the data that the Singleton might contain will be volatile. Our Finite State Manager will be a singleton, meaning to say that only one of it can exist - the FSM (we'll call it this from now on, so pay attention) will need to modify its internal state and that of the states it manages. Conversely, the states will exist within the global memory and need to modify the internal state of the FSM - indirectly, mind - when performing tasks such as telling the FSM to change state. A menu transition, perhaps?

So, how does one write a Singleton? Well, that's fairly easy. Let's outline a basic singleton and have a look at how it works.

So, we've got a class called Singleton. Singleton has a single field, a static Singleton. Singleton also has a private constructor - this means that you can't create an instance of this class (that would create some wicked cool nesting issues). Instead, to use the singleton we call Singleton.getInstance(). This returns the only singleton in existence. The getter first checks that the Singleton isn't null and, if it is, it creates it through a process known as "lazy initialisation" with "double-checked locking". After that, we have a single instance of a global object of whom we can access anything!

What kind of a tutorial would this be if we didn't create a proper example? So let's add a field (and some getters / setters) to our Singleton.

That really is bare bones, but to demonstrate a point. Something that is somewhat of a best-practice is to ensure that a singleton is initialised in something called "single-threaded space" - it's the point at which the program is running within one thread of execution. All it means for you is that nothing else can try cross-thread (get to that in a bit) access.

Above we have extended the thread class to define two threads: one that continually accesses and outputs the score value in our Singleton and another that constantly attempts to set the score. For the sake of education, we'll let these run 100 times using an int as a loop counter. Let's finish the main function in our SingletonExample class:

All we do here is create two threads - one of each of our AccessThread and OutputThread. Go ahead and run the application. What happens?

It executes out of order, right?

That's because we're only ensuring that more than one thread can't access the singleton at once. Unfortunately, this poses a problem: What if we want more than one thing to access the data, but in a specific order - one after the other?

Thread blocking

There are a couple of ways to do this, including the wait-notify method. However that's a story for another day. Within the scope of this tutorial, blocking isn't a concern. Phobos isn't multithreaded, but does need to be thread-safe due to the volatile nature of its core. What we've seen today is the basics you'll need to understand to move further. I can, however, recommend some great tutorials to those who are interested - they're at the end of this tutorial along with a complete code listing for everything we've covered.

Thank you for reading. I'm looking forward to actually finishing something for once. Check back in a couple weeks and I'll be back for more Java powered goodness. Next time, we're going to look at the JavaFX windowing system and the ways that we will be using it. In fact, in part 2 we will write the bare minimum required to make Phobos run - including the an entire FSM. Get ready!

Thanks! I'm working on Part 2 at the moment, exploring the JavaFX Scene Graph - I'm quite excited to get it out as this has been a really fun project to work on. Aiming to have it completed before the end of the week!

You raise a good point, actually. I would probably mark the syncLock object volatile as well, just for good measure, but perhaps I'm overly cautious. Still, though, that'll see itself implemented across the future of the tutorial, I'll probably get a zip file hosted with complete code after a while.

Are you really suggesting to use Singletons? Is this really the base
of your architecture? There are so many articles out there explaining why Singletons
are evil. So I am really suprised to see someone still propagating this.
You are cluttering your code with global variables (Singletons are just globals) which
of course will lead to a pure design.
Keep in my mind that there are probably junior developer reading this and might start thinking
that all of a sudden it will be fine to use Singletons.
It is not. There are very few circumstances where a Singleton might be used. Only very, very few.

Are you really suggesting to use Singletons? Is this really the base
of your architecture? There are so many articles out there explaining why Singletons
are evil. So I am really suprised to see someone still propagating this.
You are cluttering your code with global variables (Singletons are just globals) which
of course will lead to a pure design.
Keep in my mind that there are probably junior developer reading this and might start thinking
that all of a sudden it will be fine to use Singletons.
It is not. There are very few circumstances where a Singleton might be used. Only very, very few.

I'm not continuing the trend that singletons are good, the function overhead alone from getting hold of one makes me cringe. However, when working on this project, Software Design Patterns were a topic we covered and the Singleton solved a problem I was having - I wanted all of my screens to be able to interface with the Engine [i]without[/il] them having a local copy of the code. It's just how I designed the code.

With that said, I already had plans to discuss why Singletons aren't necessarily a good thing - it just made sense for the core Engine class to be a Singleton in the view that the engine is modular-ish and extensible.

[Edit] There are a million ways I could've written it without Singletons. That's just how I chose to do it. Just enjoy the tutorial...

This sounds like more bad design choices are coming up.
Every screen needs a reference to the Engine and therefore you have decided
to make it global veriable? Anyway it is up to you.
It is your code and your decisions. I just skip the next parts.

This sounds like more bad design choices are coming up.
Every screen needs a reference to the Engine and therefore you have decided
to make it global veriable? Anyway it is up to you.
It is your code and your decisions. I just skip the next parts.

That's a little harsh and, honestly, a tad insulting.

I appreciate your points and they're duly noted, but DIC is a place of help and constructive criticism. All you're doing is needlessly belittling the work I've done.

pryogene said:

Yes, actually they do. Consider that a Menu needs some way of telling the Screen Manager that it's transitioning to a new menu. I could do it this way, or I suppose I could use a boolean isTransition to flag the Screen Manager. I would still need, however, a way to add new screens onto the stack. Thus, each screen needs a way of accessing the Screen Manager. Which I integrated into my main engine class.

Whatever happens, the aim of this tutorial and subsequent ones is to impart the knowledge I gained while I built this project. Bad design choices WERE made and those will be discussed. I don't reasonably believe that this was a bad design choice. Not necessarily good, but by no means bad.

[Edit] Also, because of the JavaFX Scene Graph, this was a far nicer way to implement.

I wouldn't advice synchronizing on Singleton.class
Other classes could, in theory, be synchronizing on the same object
Instead I would have a private static object to use as lock

Seems unnecessary to me. Elaborate a little more if you would, my good fellow. /> What's the difference?

The difference is that Singleton.class can be accessed from any class, any method, everywhere
Using a private class instance, not shared with any others, will ensure no outsiders are synchronizing on the same object

I wouldn't advice synchronizing on Singleton.class
Other classes could, in theory, be synchronizing on the same object
Instead I would have a private static object to use as lock

Seems unnecessary to me. Elaborate a little more if you would, my good fellow. />/>/>/> What's the difference?

The difference is that Singleton.class can be accessed from any class, any method, everywhere
Using a private class instance, not shared with any others, will ensure no outsiders are synchronizing on the same object

I imagine this would be a quicker and less-dirty solution than thread blocking, too. I'll plug it in in a bit and see what happens. May end up updating the tutorial next time round.

[Edit] Maybe not, but it was educational to have a look-see none the less. It still serves as a more secure anti-volatility mechanism.

I imagine this would be a quicker and less-dirty solution than thread blocking, too.

You two are confusing me because you either know something about this that I don't, or you both misunderstand intrinsic locks in this situation. Only one Thread can own the lock for Singleton.class at any given time, managed by the VM. Any other thread that tries to lock on to Singleton.class, regardless of the fact that it is static, will block and wait for the lock to be released.

I just don't see a difference in this situation. Maybe you guys could explain it to me.

@farrell2k:
Locking on an object of the Singleton class ensures there's no volatility whatsoever (more or less) if nothing else can access it, whereas locking on Singleton.class doesn't necessarily have the same level of security in terms of concurrent access. I also thought that might help with the out of order execution, but I was wrong. That was all, apologies for the confusion.

The below example shows why it is a bad idea to use Singleton.class
No one are able to access the instance because BlockSingleton never leaves the monitor
Try the below code with both Singleton implementations (the one using Singleton.class and without)