so in this video we're going to use an alternative to synchronize blocks
we're going to use a fair lock now fair locks try to be first come first served
in a previous section in this course we learned about using the re entrant lock
class which implements the lock interface
now they re-entrant lock implementation allows us to create fair locks
not all lock implementations do that
the interface doesn't dictate that locks have to be fair
so always make sure to read the documentation for any lock
implementation you want to use to see what type of lock it is so let's
actually change our examples so it uses a fair re-entrant lock object rather
than a synchronized block
so first we're going to uncomment the code that sets the priorities so that
becomes active again i'm going to change the type of lock will use and will do
that up here on on line 4 so i'm going to set that to be instead of a static
object we're gonna make a static re-entrant lock
it's going to be an equal to a new re entrant lock with a parameter of true and
the parameter for true is whether it's a fair lock
so first come first served or not so true means that we are doing it that way
we want it to be set up to be a first come first served or to use a first-come
first-served order now a few important notes about using a fair re entrant lock only
fairness in acquiring the lock is guaranteed not fairness in threads
scheduling so it's possible that the thread that gets the lock will execute a
task that takes a long time
so when using fair locks it's possible for threads to still have to wait a long
time to run the only thing a fair lock guarantees is the first come first
served ordering for getting the lock
secondly the try lock method doesn't honor the fairness settings so it will not be
first come first served
and lastly when using fair locks with a lot of threads
keep in mind that performance will be impacted to ensure fairness
there has to be an extra layer of processing that manages
which thread gets the lock so that can ultimately slow things down
when there's a lot of threads competing for that lock so with that in mind let's
change our worker class to use the re-entrant lock
so what we need to do is we'll keep in the basic run and the part we have to
change is within the for loop down here instead of synchronized lock
what we're going to do is we're going to put a try block but before that will
put lock....
....and if we had some other code the execution of
critical
which I can't spell critical section of code is going to be in there as well and i will remove
the synchronized code block because we won't be using that what we do need to
do is after the try
we need to put a finally put a lock.unlock
because no matter what happens we want to make sure that the lock is released
at the end so we have to use a try finally block to guarantee that that unlock
method is actually called so that we can be sure that the lock will
ultimately be released at some point so we just try running this now we should
be able to see that the threads will now interleave much more than they
did before and you can notice the colors right away
that's completely different to what we saw last time and you have a look now
you can see that we're really getting what is a far more fair result and
notice also the priorities are actually in order if we run this again
we should get a far more consistent approach a result rather than what we
did last time
occasionally they'll be a bit of a blip where priority 10 doesn't sort of get
there first time but you can see no doubt looking at this that
the threads are really interleaving much more than they did before
so in other words we no longer have a starvation problem
no thread has to twiddle his thumbs waiting for another thread to count all
the way to a hundred
so you might be wondering at this point why you'd want to use synchronized
blocks in that case
well this is when knowing what you want to accomplish and knowing the
application is very important now if your application only uses say two or
three threads or the task
that you run each thread compete very quickly or you know from the
applications architecture that it will be rare for more than one thread to be
blocked on a lock at a time then starvation may not actually be an issue
for your code in that particular application also since using fair locks
can impact performance
you might actually decide that starvation will be the lesser of two
evils
so depending on the tasks that the threads are performing it might actually
not matter if a thread is waiting for a long time
for example you might have multiple threads pulling data of the same queue
and you don't actually care which threads pull up the data as long as it's
done quickly so in that situation using synchronize blocks would be preferable
alright so let's now move on to the next potential problem we can encounter when
working with threads live lock a live lock is similar to a deadlock
but instead of the threads been blocked they're actually constantly active and
usually waiting for all the other threads to complete their tasks
now since all the threads are waiting for others to complete
none of them can actually progress so let's say that the thread a will loop
until thread b complete its task and thread b will loop until thread a
complete its task thread a and thread b can get into a state in which they're
both looping and waiting for the other to complete
that's actually what's called a live lock the threads will never progress but
they're not actually blocked
I think an example would suffice here to make things a lot clearer
we're going to start are creating a by creating a new project
and will call this one
polite worker and as normal
choose my command line up and now I'm going to type...and
i'm going to use this window to close this close the other one down because we
don't need that anymore
alright so we're going to create 2 worker threads that share a resource
when they see that the other threads active
they're going to give the
resource to the other thread and wait for it to finish using that resource so
we need to start by creating a couple of classes
the first one is going to be the shared resource i'm going to create that that's
going to be a new Java class and it's going to be called shared resource
this will be quite a simple class we're going to have a private instance
a private field for worker which we haven't created that class yet will be doing
that shortly so I won't accept that import because we'll be creating the class
called worker in this project
actually what I might do just to save those imports is create that now so file new
Java class worker and let's put the code in for that because it's going to be
pretty straightforward
so we need a private....
....and the constructor
should accept both those and we want a getter for both
so let's just do that as well and we actually want our task
we're going to synchronize that we're going to call that task work so it's
going to be public.....and that's going to accept a shared
resource as well as a worker object.....
and the code is going to be while......
we haven't created that method yet so we'll get an error
....
which you won't bother putting any code in there for now and then we'll put a
continue in their and still within the while loop
we're going to put if...and I'll make a space down here so we can see what we're doing
.....
....
....and then we going to do shared resource....
....and then we'll do a continue....
gets down to here we're going to do a print out
of.....
....
alright so lets just swing over now to shared resource and write the
rest of the code for that then we can sort of go through what this is all doing so
we need a constructor and the constructor is going to accept a
worker object
we're going to set that and we want a getter
we want a getter and setter actually so getter and setter
so we got our get owner which returns the owner and a set owner which actually
sets the owner to what has been passed to that method and that should now
fixed up our compile errors in worker.java as you can see now the only other
thing we want to do here for set owner is we want to make sure that its a
synchronized method so synchronized we don't need to do that for getter but
because we're changing some data we want that the set owner method to be
synchronized
basically we don't want any thread interference when we are setting which
thread is currently using the resource
so now to the worker class this is also fairly
straightforward the interesting bit here is the work method so the threads going
to check to see whether it owns the shared resource if it doesn't it's gonna
wait for 10 milliseconds loop back and try again and that's what this code here
does from lines of 27 through 33 so this is waiting to see whether it's acquired the
resource and whether it basically whether it owns the shared resource now when the
condition evaluates to true meaning that it owns the shared resource it then
checks to see whether the other threads active you can see that on line 36
if it is its going to politely give the shared resource to the other thread then
return back to the beginning of the loop
now if the other thread is an active it will actually use the shared
resource and then loop back to the beginning again
so really the only time this thread will complete a loop iteration is when it
owns a shared resource and the other thread isn't active
alright so let's now add the code for the main method for their what we're
going to do is create a couple of objects
so.....
......and if you remember going back to our worker object
true was whether it's active or not
and first parameter is the actual name.....
.....and we'll call that one worker 2 and also set that to active
ok worker two and what I might do this video is getting a little bit long so I will
pause the video here or stop the video here in the next video will continue on
we'll finish off the rest of the main method and start running this and
looking at some of the issues that will come up i'll see you in the next video