Exercise 4-1. Write a Java program that takes a list of filenames on the command line and prints out the number of lines in each file. The program should create one thread for each file and use these threads to count the lines in all the files at the same time. Use
java.io.LineNumberReader
to help you count lines. You’ll probably want to define aLineCounter
class that extendsThread
or implementsRunnable
. Now write a variant of your program that uses yourLineCounter
class to read the files sequentially, rather than at the same time. Compare the performance of the multithreaded and single-threaded programs, usingSystem.currentTimeMills( )
to determine elapsed time. Compare the performance of the two programs for two, five, and ten files.Exercise 4-2. The
ThreadSafeIntList
class of Example 4-2 is a simplified version of Example 2-7 withsynchronized
methods. Another approach to synchronizing data structures is to write a wrapper class with synchronized methods that delegate to an instance of the original class. See, for example, thesynchronizedList( )
and related methods ofjava.util.Collections
. Modify the originalIntList
class (Example 2-7) to use this approach. Add a static factory method namedsynchronizedIntList( )
that takes anIntList
as its argument and returns anIntList
object that has thread-safe behavior. You’ll need to implement the wrapper class as a subclass ofIntList
, overriding its public methods to make them synchronized and make them delegate to the specifiedIntList
instance.Exercise 4-3. Example 4-4 demonstrates how deadlock can occur when two threads each attempt to obtain a lock held by the other. Modify the example to create deadlock among three threads, where each thread is trying to acquire a lock held by one of the other threads.
Exercise 4-4. Example 4-4 uses the
synchronized
statement to demonstrate deadlock. Write a similar program that causes two threads to deadlock, but usesynchronized
methods instead of thesynchronized
statement. This sort of deadlock is a little more subtle and harder to detect.Exercise 4-5. Example 4-6 shows an implementation of the Java 1.3
java.util.Timer
API. Java 1.2 introduced anotherTimer
class, thejavax.swing.Timer
class, which has a similar purpose but a different API. It invokes theactionPerformed( )
method of any number of registeredActionListener
objects one or more times after a specified delay and at a specified interval. Read the documentation for thisTimer
class, then create your own implementation of it. If you’ve read Chapter 11, you know that the methods of event listeners, such as theactionPerformed( )
method, are supposed to be invoked only by the event dispatch thread. Therefore, your implementation of theTimer
class should not invokeactionPerformed( )
directly, but should instead usejava.awt.EventQueue.invokeLater( )
orjavax.swing.SwingUtilities.invokeLater( )
to tell the event dispatch thread to invoke the method.Exercise 4-6. Once you have read Chapter 5, you may want to come back to this chapter to try this exercise. The
Server
class of Chapter 5 is a multithreaded, multiservice network server. It demonstrates important networking techniques, but it also makes heavy use of threads. One particular feature of this program is that it creates aThreadGroup
to contain all the threads it creates. ModifyServer
so that in addition to creating this one master thread group, it also creates a nested thread group for each of the individual services it provides. Place the thread for each individual client connection within the thread group for its service. Also, modify the program so that each service can have a thread priority specified and use this priority value when creating connection threads. You will probably want to store the thread group and priority for each service as fields of the nestedListener
class.
Get Java Examples in a Nutshell, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.