now we're going to talk about some common problems that can occur when
you're working with threads
we've seen in previous videos that we have to use synchronization or locks to
prevent critical sections of code from the thread interference but we're not
careful we can actually cause more problems doing that then we actually
solve the first common pitfall is called a deadlock
so a deadlock occurs when two or more threads are blocked on locks and every
thread that's blocked is holding a lock that another block thread wants
for example thread 1 is holding lock 1 and waiting to acquire lock 2 but
thread 2 is holding lock 2 and waiting to acquire lock 1 because all the
threads holding the locks are blocked they will never release the locks they're
holding and so none of the waiting threads will actually ever run
let's take a look at an example of how this works
and I've created already a new
intellij project java project called deadlock so make sure you to create that
and it's just a standard console application like we've been using in
this section and then what we're going to do is create two very simple classes
that extend the thread class we're going to start off here under main and
going to create two objects that are going to be using for our locks...
....
so we've added the two static variables to the class which the threads in the
application are going to use to synchronize so next I'm going to add
the code for the first thread and in this case it's just a small example
we're not going to worry about coloring the output so I'm just going to come up
here and in the class is going to be private....and
that's going to extend thread and then we want to have our run method
....
then we will output a message in the synchronize block thread 1 has lock 1
then we have try here and there should have been an inner class
which I will add that as an inner class
okay for try and it's going to be we're going to catch the interrupted exception
as we've done before for other threads
won't bothered processing anything in there for now but in terms of the code
that's going to run in the try block we will just put it to sleep briefly thread.sleep
we will go with a hundred and outside the thread catch we're going to put thread
1....
then we're going to do synchronized lock 2....then the code in the
synchronized block is going to be thread 1....
...if it gets to this code it will have then outside of the synchronized
block thread 1 released lock 2 and outside of the
synchronized block for lock 1 message there is going to be....
.....
ok so when the thread runs its going to try and obtain the lock for the lock 1
object if it's successful
it's going to sleep for a hundred milliseconds to give the second thread
that we will code up in a minute a chance to run when it wakes up we'll try to obtain
the lock for this lock 2 object
alright so that's what the idea is and we're going to
do pretty much the same for thread 2 going to create the private
static class again....
....this time we're going to synchronize lock 2 first
print out a message if we're successful thread 2...and we add a try
in there
in the try we are going to sleep for a hundred and catch our interrupted
exception
we won't put any code in there and then outside of the try block thread 2
.....
.....and if we're successful in a synchronized
block will put thread 2 at this point it has lock 1
technically lock 2 and 1 because that was the order that we grab them and
outside of the synchronized block 4 lock one its going to be a thread 2
2 is released
......then outside of the synchronized block for lock 2....
....
ok so two very similar classes as you can see all right so come up here now to
our main method and let's add the code to kick these off so it's going to be a
new....
....
ok so let's simulate this deadlock and see what happens i'm going to run it now
so you see what happened we can see that thread one's got lock one thread 2
got locked 2 then thread 1 message waiting for lock 2 remember
that thread 2 is already got lock two and thread two is waiting for lock
one so you can see what's happening here and at this point we don't see any other
output about the threads releasing locks and exiting when the application is not
terminated it's actually still running so we'll have to actually terminate this
manually so if we close this down now
so thinking about it it's probably not difficult to see what happened
thread one obtained lock one then called sleep and suspended thread thread 2 ran and
obtained lock 2 and then called sleep and suspended thread one woke up and
tried to obtain lock 2 but thread 2 is holding lock 2 so thread 1 blocks and
then thread 2 wakes up and tries to obtain lock 1 but thread one has
got to lock 1 already
so thread 2 blocks at this point we've got a deadlock
so in other words thread 1 will never be able to run and thread 2 we'll never
be able to run
so question here is how can we prevent deadlock situation from happening
so one solution would be any only ever tried try to lock on a single object and if
you can write the code so that only one lock is required
the code won't deadlock but unfortunately that is not a practical
solution for many applications that need multiple locks and the second way to
prevent deadlocks is to require that all threads must first try to obtain the
locks in the same order the deadlock in our example is possible because thread
one tries to obtain lock one and then lock 2
and of course thread 2 tries to obtain lock 2 and then lock one
now if we made both threads obtain the locks in the same order a deadlock
can't occur
so let's update our example here so that thread 2 tries to obtain the locks in
the same order that thread 1 does so coming back here to our thread 2 code
we're going to change this to be locked 1 now starting on lock 1 and will
change the message to say threat 2 has got lock one because that's what it is
now then moving down and thread 2 we're going to be waiting for lock
two now so change that message and we're going to synchronize now on lock 2 so
thread 2 at this point we can say i think a bit pedantic but putting it in the
right order your can say its got both lock 1 and lock 2 and here we're going to put
thread 2 is released
this will be locked 2 an outside down here thread 2 released lock one
so let's run that and see what happens so when we run the application you can
see that the applications no longer got a deadlock and you can see the print
statements and the application has terminated normally and since both
threads are now trying to obtain the locks in the same order
once the thread has obtained the outer lock the lock it tries to obtain first in
other words only that thread can obtain the inner lock
in other words it's not possible for one of the threads to hold one lock but not
the other lock this is the key to avoiding deadlocks when two or more
threads will be competing for two or more locks
you want to make sure that all threads or all the threads will try to obtain
the locks in the same order
now another solution would be to use a lock object rather than using
synchronized blocks so using the try lock method that we talked about
previously
with or without a timeout value may will prevent deadlocks but that will depend
on the code so I'm going to finish the video here now in the next video we're
going to start off by looking at another way that a deadlock can occur
see you in the next video