so now that we've added this call and created a new callable class
lets actually run this and see what happens
so we can see here that our producer and consumer threads run and then the callable
code the callable doesn't run while the producer is sleeping and the
consumers are looping because when we created the service
we passed three to the method and so the service only allows three active threads
at a time
consequently the callable had to wait till the other methods are finished
let's change that to five just to see what happens we're going to come back up
to the definition on here and we'll change that instead to 5
let's run it and notice immediately that we see the callable code run straightaway right
at the top of the screen and the output so clearly it the number of threads that
we've allocated to our thread poll made a difference and enabled the callable
code to run as well as our producer and consumer tasks so there is a lot more to
working with executor services in the future objects and i'll leave it up to
you to explore this topic further good place to start that would be the
executive service documentation so I just bring that up on the screen and
browser the link will be in the resources section you can check that out and you can
find out more information about the executor service
so before we leave our introduction to some of what's available in the
java.util.concurrent package
let's take a quick look at an example of another set of classes available in the
package that allows us to simplify our application considerably
now the package contains thread-safe queues which a perfect for a producer
consumer type applications for or situations like we've got with our
current code
now all the queue classes implement the blocking queue interface
so we are going to change our example to use an array blocking queue now it processes
elements in a FIFO order that stands for first in first out
and that's exactly what we want here because our consumers is always
remove the first element of the array list
so let's go back and look at changing some code
first we're going to change the main method we're going to create an instance
of array blocking queue rather than a array list
so close down the run window and our code here on line 15 will change that
so again instead of a array list is going to be an array
blocking queue so change this so...
....as a result of doing this we no longer need our
buffer lock
i'm going to delete that...
....because we're no longer have that
ultimately we need to change our classes as well to accommodate that change
scrolling down a little bit and looking at our producer code
it's now a lot easier let's code now
so we're going to change this to this list to instead being array blocking
queue list
we're going to delete our re-entrant lock our buffer lock we're going to change
the constructor as well so.....
....which we no longer need then...
....now incidentally array blocking queues are
bounded and that means that we have to pass in the number of elements the arrays
should be able to hold its capacity in other words to the constructor that
don't grow like arrays lists do now since the producer could potentially
add the strings 1 to 5 and in a file before any consumers have a chance to remove
elements
we're going to create a array blocking queue that can hold six elements
so probably should have done that earlier so go back up to the top where we
defined it and I set that up there
to 6
and for our run method that's now going to be dramatically simplified we're
going to remove the buffer lock code going to come down here and we're going
to leave our try loop in but we're going to change it a little bit so what we're
going to do is leave the adding then we just need to put buffer....
we don't need any of this other catch block code in there will leave the
sleep in there and we'll leave in the interrupted exception to catch because we
still need to do that in case the thread is interrupted
moving on down we're going to delete the buffer lock down here we're going to
leave the try block but that's instead of an add its going to be a put...which is
how we add an element to an array blocking queue going to get rid of the
unlock which was for the old buffer lock which we removed
i'm going to change that to an interrupted exception...
...I won't put any code for now
so that's now our new my producer code
so the blocking queue in this case the array blocking queue
you can see that we're using .put and we also use remove and take methods as
well
now the add and remove methods would throw an exception if the operation can't
be performed immediately because in other threads got the queue locked the
put and take methods will block when the queue is locked so that's the reason
I'm using a put there is that it will automatically block for us rather than
throwing an exception
now the same applies when the queue is full or empty the add and remove methods
were thrown exception and the put and take methods will just do a block
now if we didn't want to check for the end of file string without removing
it from the queue we wouldn't need to call is empty anymore
we could instead just call take and block until something's in the queue but
the method we used to check for end of file doesn't block so we still need to
take elements off the queue using 2 method calls
checking for empty and actually removing an element when the queue is empty now
we want the producers and consumers to block when the queue is locked by
another thread
and that's why I'm using the put method to add elements to the queue and shortly
when I update the consumer class i'm going to use the take method to remove
elements from the queue
you might also be wondering about synchronization with put well we don't
have to worry about synchronizing calls to put because it's a thread safe method
so we don't need to put a synchronized block or a lock objects and that's why i
was able to remove that
so you can see that by doing that greatly simplifies the producer code but
we still need to leave the catching because thread can be interrupted while
it's blocked on a put call so we have to handle interrupted
exceptions when we do call put all right so let's move on to the consumer code
and update that
so I'm going to get rid of buffer lock all together because you won't be using that
we need to change list to....
....
...we can greatly simplify this as well
we got an if in there at the moment so we're just going to remove the if
.....i'm going to do a first we are gonna check if buffer is empty
we only need to do the continue we don't need to do anything extra there
what we can do here actually lets just remove the counter we
don't really need to do that anymore we saw how that worked previously and i'll
delete the printouts of the counter
instead of using buffer.get we're going to use buffer.peak
so if buffer.peak we don't need to specify the element if that equals end of
file then we do the normal processing to exit
else what we're going to do is we're going to remove the item we're going to
use .take remembering that take actually blocks for us automatically so
instead of a finally what we want to do is catch the interrupted exception and I
can just delete the rest of that code and leave that one also for our while
loop
so you can see that I've now remove the locking code as well as the counting
code and i'm using the tape method to remove elements and you might be
wondering how come we don't need to specify the element anymore
this is in the .take method and also in the .peak method well
remembering that the array blocking queue is a FIFO queue first in first out
so we don't need to specify that we want the first element it automatically knows
to take that element out
speaking of the peak method when we check for end of file using that method
and let's just look at the next element without actually removing it from the
queue
remember that we need to leave the end of file in the queue for the other
consumer the peak method doesn't block when the queue is empty
it returns null and that's why we still have to check to see whether the queue is empty
so let's run this and see what happens
notice that we've got an error there and that's probably gonna be a null pointer
exception...
depending on timing in might or might not see this null pointer exception in the
consumer code and you might need to re run it to see that that comes up
so why are we getting this error in the consumer code as you can see that we
were crushing on line 93 which is clearly part of our consumer class now
isn't the queue thread-safe so why are we actually getting this error well
remember that thread safe means that we can be confident that our call to one of
the class methods will complete before another thread can run a method in the
class so the producer code only calls the put method and the code
following the put doesn't depend on the result of the put
however in the consumer code what we're doing is we check to see if the queue is
empty
if it isn't we then peak at the next element but the consumer thread can be
suspended between calling is empty and calling peak while it suspended the
other consumer thread can run and take the next element from the queue when the
suspended consumer thread runs again peak returns null and consequently we
get a null pointer exception when we call the equals method and that's what
happened in this scenario because you can see that the errors on line 93
which is the peak method so what that all means is that we may still have to add
synchronization code when using thread-safe classes like array blocking
queue
so let's add a synchronized block and synchronized on the queue so the best
place to put that would be under the while and put synchronized....
then we'll put the rest of our code in that synchronize block
run that and we're just running a few times just to be sure that we're not
going to be getting that null pointer exception any more one more time so we
can see that our application is now not throwing any exceptions
so we still need some synchronization but by using one of the queues in the java
util concurrent package
we didn't have to synchronize the producer code at all
so that's it for the java.util.concurrent package
there are lots of classes and interfaces in the package so definitely take a look
at what's available when you need to synchronize code especially when you're
working with an application that's going to be using a lot of threads so in the
next video we're going to look at some common problems that can arise when
using threads
see you in the next video