let's talk about the next problem that we're going to do or that can
arise when working with threads
that's called starvation now when starvation occurs it's not that threads
will never progress because they'll never get a lock but that they rarely
have the opportunity to run and progress
so starvation often occurs due to thread priority when we assign a high priority
priority as to a thread we are suggesting to the operating system that it should try
and run the thread before other waiting threads
remember that when we use synchronized blocks it's not
first come first served when there's more than one thread waiting for a lock
and that lock becomes available
the operating system won't necessarily choose the thread that has been waiting the
longest to run a thread can block on the lock first but that doesn't mean it will
be the first thread to run when the lock becomes available
so it's possible that the thread won't be able to run for a long time because
other threads keep blocking on the lock and when the lock becomes available
the operating system chooses one of those other threads to run especially
when one of the other threads has a higher priority than the first thread
that blocked
let's look at an example just to make sense of this we're going to create five
threads and assign them at different priorities
the threads are going to print out the numbers one to a hundred to the console
let's start by creating a new console project file new project and
unsurprisingly given that we're talking about starvation will refer to it as
starvation or call the project starvation finish and replace
this window because we don't need to the say hello project anymore and i'm just
going to pull this over to the left a little bit
ok just to make a bit more space here for typing so what we do first is will
add our thread color class to the project
of course we've used that thread color thread color class before
so you can grab the code in the resources section for this video I'm
just going to create the actual class and then paste in the values so it's
new
java class i'm going to call this one thread color as we've done before and
I'm just going to paste in the values which are in the resources section
all right so going back to our main class we want to start off by adding a static
variable
we're going to use to synchronize code so we're going to just put that in the
main as a field
....
again we are going to use that to synchronize our code now we need to add
a worker class as an inner class of the main class this will contain the code
that each thread will ultimately run
let's put that down below the main method within our class as an inner class so
we are going to do
....
.....
the constructor is going to just accept the color as a parameter only
so just that one click OK and then we want our run method obviously so.....
....
....and lets write the code for that
and again I mentioned that it's going to count from 1 to 100.....
....
.....and we want to also write our run count and we
also want to increment that after displaying that on the screen or outputting it
to the console I should say and put a comment in here to execute
a critical section of code which would where that would ultimately be so when we
construct an instance of the class we are going to pass the thread color that we want to
use when the thread runs this code it should loop a hundred times and each time it
loops
it's going to print out the value of the loop counter when we call systems.out.format
its current value is stored when the string is formatted and then
the values incremented as you saw on line 22 now as you see in a second
we're going to create a worker object for each thread because each thread
will use its own instance of the worker class
the threads won't interfere with each other as they count to a hundred the
code in the run method isn't a critical section but will pretend that their is some
code after the format call that requires synchronization and that's where you saw
me put that comment on line 23
for example we could be calling a method in a class that perhaps isn't thread safe
so now that we've done that let's add the code to the main method to kick
these threads off
we're going to come back up here and we're going to put.....
....
and we'll call this one priority 10 and it's lets take a copy of this and create
another four threads need to change the names of them....
....
we'll just change the first one to red....
we'll just change colors if we need to second one will go with blue....
the third one let's go with green and we will do cyan for the fourth one and lets use
purple for fifth ones so priority 10 it will go
...and then what we need to do is do a t1.setpriority for each one of
these over so for t1 we set priority 10 as the text that's going to be displayed so we
need to make sure we do that as well and then we'll do the same for the other
four making sure that we set the priority to the same level as I've
mentioned in the string variable that we're passing as a parameter so....
....
obviously the last thing to do is to t1.start and we do that for the remaining
four as well
....
ok so we gave each thread a name when we construct a thread instance
on line 7 to 11
we then a set a priority for each one using the set priority method which is
so we do on line 13 to 17 and keep in mind that the priority is only a
suggestion to the operating system
it's not actually binding in any way we can never force the operating system to
run threads in a specific order
so you may look at this code now be wondering how all the threads could
block on the same lock object since we're passing a different worker object
to each thread
indeed if you recall in the previous video we had an issue for that same
reason we're actually passing objects a different object and we're getting a
deadlock for that reason
so it's true that we are passing a different worker object to each thread
but the lock object were using is static you can see that on line 4
so in other words is only one instance of the object all the threads will be
competing therefore for that one instances lock
also note that the lock on the object will be frequently released giving any
blocked threads plenty of opportunity to run
so let's actually run this code and see what happens
and again it will depend on timing but we'll probably see is that the thread
with priority 10 which is the one that we assign red will print out the
numbers one to a hundred before any other thread has a chance to run after
that it's purely up to the operating system as to what will happen
it may run the thread with priority 8 but we'll see what happens anyway
let's just try this and see what happens
and you can see this first time when we run it priority 10 got from numbers 1 to
28 then purple kicked in
even though it's got a priority 2 as you can see here and scrolling down a
little bit further
purple got to a hundred green or cyan I should is got to go
then we got to green as you can see for priority 6 priority 8 has a chance with
blue and then priority 10 got the rest but let's just try running it again
so you can see that I'm getting different results each time I run it even
though we've assigned priority 10 you can see this particular case here
priority 10 got from one to a hundred and then purple got priority 2 which
you would think that priority 8 6 and 4 might have a go beforehand but
that's not necessarily the case
just run it one more time and you can see that priority 10 has only got a run
count of 3 that time so that's purely up to the operating system it may run the
thread the second one with priority 8 but that's not necessarily the
case
so its a little bit bizarre when you look at it when you continually run this
you get different results and there we've got another result there we're
priority 10 as you think would happen goes from one to a hundred before the
others get a chance so more than likely you will you get different results on your
computer then what I'm showing you on the screen here now so you can see
that it
the order that we've kick things off with our threads isn't necessarily the order
that the operating system is going to run it
even though we've told the operating system or given a suggestion for the
priority that we want these threads to run
now thread may only be suspended once in the middle of counting or not
in other words each thread gets to run for quite a long time in computer time
generally speaking
while the other threads are stuck waiting now the key takeaway for all this
here is that although this
the lock is released frequently often the highest priority thread will
probably hog all the time until it's finished counting and the next thread
that runs won't necessarily be the thread with priority
8 and you obviously saw that and also the last thread to terminate won't
necessarily be the thread
with priority 2 and again you saw that as well we've got this on the screen now
we're going go down and have a look right at the end priority 6 was the one that
finished in this run so it isn't a first-come first-serve system now again
this is a trivial example so it doesn't matter that one thread hogs all the time
but in a real world application the tasks that a thread is performing might
you know perhaps be a long-running task
what if the run method was performing database queries or file reads if the
threads block there for seconds or even minutes because other threads are always
chosen to run when the lock becomes available then the applications
performance will definitely be adversely affected
also as we've just seen setting a priority really is just a suggestion to
the operating system
you saw when i ran the code multiple times that we've got different results
and priority 10 even wasn't necessarily finished in completely each time it's
always some other thread that will kick in and we usually find often found
that priority 2 got the go after priority 10 for some reason so we
started again you can see priority 10 always gets the first but then it seems
to be priority 2 always comes in next and again that might be different
for your computer now you might be thinking at this point the reason that
the thread with priority 10 ran first was because we assigned it on line 19 as
that if we change these around a little bit t1 will make that t3...
and leave this t1 so we are starting the thread with the highest priority last
effectively on line 23
if you run that to see what happens this will depend on timing again and if we
just run this we can see that priority 6 has got in first run but run this again
it will actually depend on timing and even though i'm getting the same result each
time you might well find that you get a different result of this depending on
the order that you set this to on your computer
what you should be able to find is if you run this a few times that you
will get priority 10 to start sometimes when you change the order of the
execution here
but of course when i'm recording a video is not doing that for me now we've got
priority 10 got nearly got to the first go so I'll just try one more time and see
whether we can get it
we got priority 10 to come up second their and again you can see the results
are different each time effectively when we are running it
so you can see that priority has got an effect on the order
once we specified that but it's really loosely if the task wasn't so
trivial the effect on setting a priority might be more pronounced that's the
other thing to keep in mind because obviously doing just a little bit of
code here just sort of executing for the numbers one to a hundred and looping
through them
so we've seen what starvation can do because the synchronized blocks aren't
first come first served setting priorities can result in starvation
threads can languish even with when a lock is frequently released
even when we don't set priorities will this still be true
so let's actually try not setting priorities and see what happens
so I'm going to just comment that out for now the priorities that we defined
and lets see when we run that what happens
so we got priority 6 working first we've got priority 10 kicks in then
but notice they have all gone to a hundred this time we'll just run this a few more
times the same result there so getting a bit more consistency there
we're getting priority 4 cyan always appearing last
so you can still see the threads aren't necessarily running in the
right the same order that we started on that because t3 is our priority 6 and
that actually started first that was ok but t1 at the end is priority 10 that
clearly didn't run last did it you can see that the last we've got our priority
4 which is t3
sorry our t4 and that was second to last but that finish last
so setting priorities can make starvation more likely to happen
so how can we actually prevent this starvation so when we're dealing with
deadlocks the order in which locks are required was important but the
starvation which thread gets to run when a lock becomes available is
important
so ideally would like things to be first come first served
because we can't dictate when threads will run we can't guarantee a first-come
first-serve order but we can certainly do our best
I'm going to finish the video now in the next video we're going to actually talk
about finding a way to get around this
instead of using synchronized blocks we're going to use something called a
fair lock so let's talk about that in the next video