By Tony Bevis on Sunday, 15 October 2023
Category: Java

Java programming course: 12.2 Extending the Thread class

In the previous lesson we learnt about the concepts of multithreading.

Extending the Thread class

The first way of defining a multithreaded class is to extend the Thread class and override its run() method. Here is the Booker class which does just that:


Here is the code needed to simulate 100 separate persons (each of whom will be a threaded object of the Booker class) each making one booking. You could enter this in the Experiments class to run it:


The number of bookings output should of course be 100, since that was how many Booker objects made a single booking each. But if you run the above code, you will almost certainly find it outputs a number smaller than 100, possibly much smaller. It is also possible that you will get different results each time you run it. So, what is causing this to happen? To answer this question, it will be helpful to see diagrammatically how a thread transitions between its various states:

The above diagram looks a little daunting at first, but here is a descriptive summary:


Now look again at the pertinent code inside the makeBooking() method of BookingCounter:

Suppose there are only two separate Booker threads that each need to execute the above code. When the first thread moves into the running state it needs to execute statements 1, 2 and 3 above but it is possible that it might only complete statement 1 before the scheduler decides to pause it, giving control to the second thread. At this stage, the count variable has not had a chance to be incremented so will be at its initial value of zero.

Now, the second thread starts and when it executes statement 1 finds that count is still zero. Suppose it then goes on to complete statements 2 and three, leaving count with the value of 1.

Now, the scheduler passes control back to the first thread where it will carry on from where it left off, but its object copyCount is still zero so it when it completes statements 2 and 3 count ends up being 1 again, instead of 2 which it should be.

You might think that replacing the above three statements with the one below would solve the problem:

However, a single Java statement could still require multiple machine code instructions to do its work, and the scheduler might pause it before they complete. Taking the incrementation above, the machine code still needs three processes to achieve this:

  1. Read the current value from memory
  2. Update the value
  3. Write the new value back to memory

You may find that using count++ as the only statement inside makeBooking() appears to work – but you cannot guarantee that it always will.

The solution is to lock the object such that other threads won't be allowed to gain access until it completes. To do this you need to specify the synchronized keyword as part of the method definition:

Once the first thread gains access to a synchronized method it puts a lock on the object the method belongs to, such that no other thread can access any synchronized method in that object until the first thread completes. In this way, you know the count variable will be updated properly for each thread, and you should now find that executing the code results in the correct output of 100.

To summarise, any method which returns or changes an instance variable, and which could be invoked by multiple threads, should be marked as synchronized. This ensures that a lock is placed upon the object by the invoking thread and the lock only gets released when that thread completes all of the code within the method. This means that no other thread will be able to access either that or any other synchronized method until the lock is released. Methods which aren't marked as synchronized will still be available to other threads.

Use of synchronisation does have a performance impact, so you should avoid using the synchronized keyword if you know it can never be invoked by multiple threads.

It is also possible to synchronize blocks of code within a method rather than an entire method.

In the next lesson we will show how to create a thread through implementing the Runnable interface.

Next lesson: 12.3 Implementing the Runnable interface

Related Posts

Leave Comments