
- •Contents at a Glance
- •Contents
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Introduction
- •Oracle Java Certifications: Overview
- •FAQ 1. What are the different levels of Oracle Java certification exams?
- •FAQ 4. Is OCPJP 7 prerequisite for other Oracle certification exams?
- •FAQ 5. Should I take the OCPJP 7 or OCPJP 6 exam?
- •The OCPJP 7 Exam
- •FAQ 7. How many questions are there in the OCPJP 7 exam?
- •FAQ 8. What is the duration of the OCPJP 7 exam?
- •FAQ 9. What is the cost of the OCPJP 7 exam?
- •FAQ 10. What are the passing scores for the OCPJP 7 exam?
- •FAQ 11. What kinds of questions are asked in the OCPJP 7 exam?
- •FAQ 12. What does the OCPJP 7 exam test for?
- •FAQ 13. I’ve been a Java programmer for last five years. Do I have to prepare for the OCPJP 7 exam?
- •FAQ 14. How do I prepare for the OCPJP 7 exam?
- •FAQ 15. How do I know when I’m ready to take the OCPJP 7 exam?
- •Taking the OCPJP 7 Exam
- •FAQ 16. What are my options to register for the exam?
- •FAQ 17. How do I register for the exam, schedule a day and time for taking the exam, and appear for the exam?
- •The OCPJP 7 Exam: Pretest
- •Answers with Explanations
- •Post-Pretest Evaluation
- •Essentials of OOP
- •FunPaint Application: An Example
- •Foundations of OOP
- •Abstraction
- •Encapsulation
- •Inheritance
- •Polymorphism
- •Class Fundamentals
- •Object Creation
- •Constructors
- •Access Modifiers
- •Public Access Modifier
- •Private Access Modifier
- •Protected and Default Access Modifier
- •Overloading
- •Method Overloading
- •Constructor Overloading
- •Overload resolution
- •Points to Remember
- •Inheritance
- •Runtime Polymorphism
- •An Example
- •Overriding Issues
- •Overriding: Deeper Dive
- •Invoking Superclass Methods
- •Type Conversions
- •Upcasts and Downcasts
- •Casting Between Inconvertible Types
- •Using “instanceof” for Safe Downcasts
- •Java Packages
- •Working with Packages
- •Static Import
- •Summary
- •Abstract Classes
- •Points to Remember
- •Using the “final” Keyword
- •Final Classes
- •Final Methods and Variables
- •Points to Remember
- •Using the “static” Keyword
- •Static Block
- •Points to Remember
- •Flavors of Nested Classes
- •Static Nested Classes (or Interfaces)
- •Points to Remember
- •Inner Classes
- •Points to Remember
- •Local Inner Classes
- •Points to Remember
- •Anonymous Inner Classes
- •Points to Remember
- •Enum Data Types
- •Points to Remember
- •Summary
- •Interfaces
- •Declaring and Using Interfaces
- •Points to Remember
- •Abstract Classes vs. Interfaces
- •Choosing Between an Abstract Class and an Interface
- •Object Composition
- •Composition vs. Inheritance
- •Points to Remember
- •Design Patterns
- •The Singleton Design Pattern
- •Ensuring That Your Singleton Is Indeed a Singleton
- •The Factory Design Pattern
- •Differences Between Factory and Abstract Factory Design Patterns
- •The Data Access Object (DAO) Design Pattern
- •Points to Remember
- •Summary
- •Generics
- •Using Object Type and Type Safety
- •Using the Object Class vs. Generics
- •Container Implementation Using the Object Class
- •Container Implementation Using Generics
- •Creating Generic Classes
- •Diamond Syntax
- •Interoperability of Raw Types and Generic Types
- •Generic Methods
- •Generics and Subtyping
- •Wildcard Parameters
- •Limitations of Wildcards
- •Bounded Wildcards
- •Wildcards in the Collections Class
- •Points to Remember
- •The Collections Framework
- •Why Reusable Classes?
- •Basic Components of the Collections Framework
- •Abstract Classes and Interfaces
- •Concrete Classes
- •List Classes
- •ArrayList Class
- •The ListIterator Interface
- •The LinkedList Class
- •The Set Interface
- •The HashSet Class
- •The TreeSet Class
- •The Map Interface
- •The HashMap Class
- •Overriding the hashCode() Method
- •The NavigableMap Interface
- •The Queue Interface
- •The Deque Interface
- •Comparable and Comparator Interfaces
- •Algorithms (Collections Class)
- •The Arrays Class
- •Methods in the Arrays Class
- •Array as a List
- •Points to Remember
- •Summary
- •Generics
- •Collections Framework
- •Processing Strings
- •String Searching
- •The IndexOf() Method
- •The regionMatches() Method
- •String Parsing
- •String Conversions
- •The Split() Method
- •Regular Expressions
- •Understanding regex Symbols
- •Regex Support in Java
- •Searching and Parsing with regex
- •Replacing Strings with regex
- •String Formatting
- •Format Specifiers
- •Points to Remember
- •Summary
- •Reading and Writing from Console
- •Understanding the Console Class
- •Formatted I/O with the Console Class
- •Special Character Handling in the Console Class
- •Using Streams to Read and Write Files
- •Character Streams and Byte Streams
- •Character Streams
- •Reading Text Files
- •Reading and Writing Text Files
- •“Tokenizing” Text
- •Byte Streams
- •Reading a Byte Stream
- •Data Streams
- •Writing to and Reading from Object Streams: Serialization
- •Serialization: Some More Details
- •Points to Remember
- •Summary
- •A Quick History of I/O APIs
- •Using the Path Interface
- •Getting Path Information
- •Comparing Two Paths
- •Using the Files Class
- •Checking File Properties and Metadata
- •Copying a File
- •Moving a File
- •Deleting a File
- •Walking a File Tree
- •Revisiting File Copy
- •Finding a File
- •Watching a Directory for Changes
- •Points to Remember
- •Summary
- •Introduction to JDBC
- •The Architecture of JDBC
- •Two-Tier and Three-Tier JDBC Architecture
- •Types of JDBC Drivers
- •Setting Up the Database
- •Connecting to a Database Using a JDBC Driver
- •The Connection Interface
- •Connecting to the Database
- •Statement
- •ResultSet
- •Querying the Database
- •Updating the Database
- •Getting the Database Metadata
- •Points to Remember
- •Querying and Updating the Database
- •Performing Transactions
- •Rolling Back Database Operations
- •The RowSet Interface
- •Points to Remember
- •Summary
- •Define the Layout of the JDBC API
- •Connect to a Database by Using a JDBC driver
- •Update and Query a Database
- •Customize the Transaction Behavior of JDBC and Commit Transactions
- •Use the JDBC 4.1 RowSetProvider, RowSetFactory, and RowSet Interfaces
- •Introduction to Exception Handling
- •Throwing Exceptions
- •Unhandled Exceptions
- •Try and Catch Statements
- •Programmatically Accessing the Stack Trace
- •Multiple Catch Blocks
- •Multi-Catch Blocks
- •General Catch Handlers
- •Finally Blocks
- •Points to Remember
- •Try-with-Resources
- •Closing Multiple Resources
- •Points to Remember
- •Exception Types
- •The Exception Class
- •The RuntimeException Class
- •The Error Class
- •The Throws Clause
- •Method Overriding and the Throws Clause
- •Points to Remember
- •Custom Exceptions
- •Assertions
- •Assert Statement
- •How Not to Use Asserts
- •Summary
- •Introduction
- •Locales
- •The Locale Class
- •Getting Locale Details
- •Resource Bundles
- •Using PropertyResourceBundle
- •Using ListResourceBundle
- •Loading a Resource Bundle
- •Naming Convention for Resource Bundles
- •Formatting for Local Culture
- •The NumberFormat Class
- •The Currency Class
- •The DateFormat Class
- •The SimpleDateFormat Class
- •Points to Remember
- •Summary
- •Introduction to Concurrent Programming
- •Important Threading-Related Methods
- •Creating Threads
- •Extending the Thread Class
- •Implementing the Runnable Interface
- •The Start( ) and Run( ) Methods
- •Thread Name, Priority, and Group
- •Using the Thread.sleep() Method
- •Using Thread’s Join Method
- •Asynchronous Execution
- •The States of a Thread
- •Two States in “Runnable” State
- •Concurrent Access Problems
- •Data Races
- •Thread Synchronization
- •Synchronized Blocks
- •Synchronized Methods
- •Synchronized Blocks vs. Synchronized Methods
- •Deadlocks
- •Other Threading Problems
- •Livelocks
- •Lock Starvation
- •The Wait/Notify Mechanism
- •Let’s Solve a Problem
- •More Thread States
- •timed_waiting and blocked States
- •waiting State
- •Using Thread.State enum
- •Understanding IllegalThreadStateException
- •Summary
- •Using java.util.concurrent Collections
- •Semaphore
- •CountDownLatch
- •Exchanger
- •CyclicBarrier
- •Phaser
- •Concurrent Collections
- •Apply Atomic Variables and Locks
- •Atomic Variables
- •Locks
- •Conditions
- •Multiple Conditions on a Lock
- •Use Executors and ThreadPools
- •Executor
- •Callable, Executors, ExecutorService, ThreadPool, and Future
- •ThreadFactory
- •The ThreadLocalRandom Class
- •TimeUnit Enumeration
- •Use the Parallel Fork/Join Framework
- •Useful Classes of the Fork/Join Framework
- •Using the Fork/Join Framework
- •Points to Remember
- •Summary
- •Using java.util.concurrent Collections
- •Applying Atomic Variables and Locks
- •Using Executors and ThreadPools
- •Using the Parallel Fork/Join Framework
- •Chapter 3: Java Class Design
- •Chapter 4: Advanced Class Design
- •Chapter 5: Object-Oriented Design Principles
- •Chapter 6: Generics and Collections
- •Chapter 7: String Processing
- •Chapter 8: Java I/O Fundamentals
- •Chapter 9: Java File I/O (NIO.2)
- •Chapter 10: Building Database Applications with JDBC
- •Chapter 11: Exceptions and Assertions
- •Chapter 12: Localization
- •Chapter 13: Threads
- •Chapter 14: Concurrency
- •OCPJP7 Exam (1Z0-804 a.k.a. Java SE 7 Programmer II) Topics
- •OCPJP 7 Exam (1Z0-805, a.k.a. Upgrade to Java SE 7 Programmer) Topics
- •Answers and Explanations
- •Answer Sheet
- •Answers and Explanations
- •Index

Chapter 13 ■threads
In the case of a single thread waiting for a lock, there is no significant difference between notify() and notifyAll(). however, when there is more than one thread waiting for the lock, in both notify() and notifyAll(), the exact thread woken up is under the control of the JVM and you cannot programmatically control waking up a specific thread.
at first glance, it appears that it is a good idea to just call notify() to wake up one thread; it might seem unnecessary to wake up all the threads. however, the problem with notify() is that the thread woken up might not be the suitable one to be woken up (the thread might be waiting for some other condition, or the condition is still not satisfied for that thread etc). In that case, the notify() might be lost and no other thread will wake up potentially leading to a type of deadlock (the notification is lost and all other threads are waiting for notification—forever!).
to avoid this problem, it is always better to call notifyAll() when there is more than one thread waiting for a lock (or more than one condition on which waiting is done). the notifyAll() method wakes up all threads, so it is not very efficient. however, this performance loss is negligible in real world applications.
prefer notifyAll() to notify().
Using notify()/notifyAll() will wake up only threads waiting on the lock on which it is called; it will not wake up any other threads. If by mistake you use wait() on one lock and notify()/notifyAll() on another lock, the waiting thread will never get notified and the program will hang (leading to one kind of deadlock situation)!
Let’s Solve a Problem
Since the wait/notify mechanism is important to understand, let’s take another example and try to understand it more rigorously.
Problem Statement: Assume that you need to implement a dice player game. This is a two player game (say the players are “Joe” and “Jane”) where the players throw the dice on their turns. When one player throws the dice, another player waits. Once the player completes throwing, he/she informs the other player to play; after that, he/she starts waiting for the other player to throw the dice. You need to implement these two players as two threads working together. The game ends after each player throws 6 times (so there will be a total of 12 throws in the game).
Since the problem statement says “implement these two players as two threads working together,” your solution is a multi-threaded program with each player implemented as a thread. The problem also states that when one player throws the dice, another waits. So, you should perhaps use a wait/notify mechanism. The dice rolling should result in a random value, so you can use the Random class for creating random numbers from 1 to 6.
420
Chapter 13 ■Threads
Here is a solution. First go through the whole program (Listing 13-15), and then you’ll see the explanation of how it works.
Listing 13-15. DiceGame.java
import java.util.Random;
// the Gamers class just holds the name of players who roll the dice class Gamers {
// prevent instantiating this utility class by making constructor private private Gamers() {}
public static final String JOE = "Joe"; public static final String JANE = "Jane";
}
// the Dice class abstracts how the dice rolls and who plays it class Dice {
// to remember whose turn it is to roll the dice private static String turn = null;
synchronized public static String getTurn() { return turn; } synchronized public static void setTurn(String player) { turn = player; }
// which player starts the game
public static void setWhoStarts(String name) { turn = name; }
// prevent instantiating the class by making it private (we've only static members) private Dice() { }
//when we roll the dice, it should give a random result private static Random random = new Random();
//random.nextInt(6) gives values from 0 to 5, so add 1 to result in roll() public static int roll() { return random.nextInt(6) + 1; }
}
//the class Player abstracts a player playing the Dice game
//each player runs as a separate thread, so Player extends Thread class class Player extends Thread {
private String currentPlayer = null; private String otherPlayer = null;
public Player(String thisPlayer) { currentPlayer = thisPlayer;
//we've only two players; we remember them in currentPlayer and otherPlayer otherPlayer = thisPlayer.equals(Gamers.JOE) ? Gamers.JANE: Gamers.JOE;
}
public void run() {
// each player rolls the dice 6 times in the game for(int i = 0; i < 6; i++) {
// acquire the lock before proceeding synchronized(Dice.class) {
421
Chapter 13 ■Threads
//if its not currentPlayer's turn, then
//wait for otherPlayers's notification while(!Dice.getTurn().equals(currentPlayer)) {
try {
Dice.class.wait(1000); System.out.println(currentPlayer +
"was waiting for " + otherPlayer);
}
catch(InterruptedException ie) { ie.printStackTrace();
}
}
// its currentPlayer's turn now; throw the dice System.out.println(Dice.getTurn() + " throws " + Dice.roll()); // set the turn to otherPlayer, and notify the otherPlayer Dice.setTurn(otherPlayer);
Dice.class.notifyAll();
}
}
}
}
// class DiceGame just starts the game by starting player threads class DiceGame {
public static void main(String []s) {
Player player1 = new Player(Gamers.JANE); Player player2 = new Player(Gamers.JOE);
// don't forget to set who starts the game Dice.setWhoStarts(Gamers.JOE); player1.start();
player2.start();
}
}
When you run the program, the sample output will be like this:
Joe throws 2
Jane was waiting for Joe Jane throws 5
Joe throws 6
Jane was waiting for Joe Jane throws 1
Joe throws 2
Jane was waiting for Joe Jane throws 6
Joe throws 6
Jane was waiting for Joe Jane throws 5
Joe was waiting for Jane Joe throws 5
Jane was waiting for Joe
422
Chapter 13 ■Threads
Jane throws 4
Joe was waiting for Jane Joe throws 4
Jane was waiting for Joe Jane throws 5
Now, let’s look at the code in more detail to understand how it works.
// the Gamers class just holds the name of players who roll the dice class Gamers {
// prevent instantiating this utility class by making constructor private private Gamers() {}
public static final String JOE = "Joe"; public static final String JANE = "Jane";
}
The class Gamers is just a utility class that holds the name of the players (Joe and Jane). Since there is no need to instantiate the class, you declare the constructor private.
The class Dice abstracts how the dice are rolled; it also remembers the turns that the players take.
class Dice {
// to remember whose turn it is to roll the dice private static String turn = null;
synchronized public static String getTurn() { return turn; } synchronized public static void setTurn(String player) { turn = player; }
// which player starts the game
public static void setWhoStarts(String name) { turn = name; }
// prevent instantiating the class by making it private (we've only static members) private Dice() { }
//when we roll the dice, it should give a random result private static Random random = new Random();
//random.nextInt(6) gives values from 0 to 5, so add 1 to result in roll() public static int roll() { return random.nextInt(6) + 1; }
}
You have a member named turn of type String. This variable holds the name of the current player whose turn has come to roll the dice. The method getTurn() and setTurn() are getter and setter methods for this member. When the game starts, you should say who should start the game (you need to set turn to a proper initial value); you do it by calling setWhoStarts. All the members in the class are static, so there is no need to instantiate the class; you enforce this by making the constructor private.
The dice rolling should result in a random value in the range 1 to 6. You can use the Random class in the java.util package to get the random number. The Random class has an instance method of nextInt() that you can use to get the range of values you want. If you pass int value 6 to nextInt, it returns the values from 0 to 5, so you add 1 to get the value ranging from 1 to 6.
The Player class is where you do most of the work. The class Player abstracts a player playing the Dice game. Each player runs as a separate thread, so Player extends the Thread class. Alternatively, you could implement Player by implementing the Runnable interface. Both are equivalent and acceptable solutions.
class Player extends Thread { |
|
private String currentPlayer = null; |
|
private String otherPlayer = null; |
|
|
423 |
Chapter 13 ■Threads
public Player(String thisPlayer) { currentPlayer = thisPlayer;
// we've only two players; we remember them in currentPlayer and otherPlayer otherPlayer = thisPlayer.equals(Gamers.JOE) ? Gamers.JANE: Gamers.JOE;
}
// other members
}
You create two Player threads for each of the players. So, you remember the values in currentPlayer and otherPlayer; you set these values in the Player constructor.
Here is the Player's run() method:
public void run() {
// each player rolls the dice 6 times in the game for(int i = 0; i < 6; i++) {
// acquire the lock before proceeding synchronized(Dice.class) {
//if its not currentPlayer's turn, then
//wait for otherPlayers's notification while(!Dice.getTurn().equals(currentPlayer)) {
try {
System.out.println(currentPlayer +
"waiting for " + otherPlayer);
Dice.class.wait(1000);
}
catch(InterruptedException ie) { ie.printStackTrace();
}
}
//its currentPlayer's turn now; throw the dice System.out.println(Dice.getTurn() +
" throws " + Dice.roll());
//set the turn to otherPlayer, and notify the otherPlayer Dice.setTurn(otherPlayer);
Dice.class.notifyAll();
}
}
}
The run() method will be called for each Player thread. Each player rolls the dice six times, so you have a for loop with six iterations. In every loop iteration, you check if it’s the currentPlayer's turn to roll the dice. If not, you make the player thread wait till the otherPlayer informs the currentPlayer that his/her turn has come. Before going to check the turn, you need to acquire a lock. Any common lock is good, and you use the Dice.class as the lock here. Once the currentPlayer gets the notification, he/she calls the Dice.roll() method. His/her turn is over now, so he/ she sets the turn to the other player and calls notifyAll() to wake up the otherPlayer thread. You could have used the notify() method, but it is equally acceptable to use the notifyAll() method, which is better to use.
The DiceGame class does something very simple. It has the main() method and you create the Jane and Joe player objects. You set one of them to start the game. You call the start() methods for these two player threads to start playing.
424