3.5 Lock Scope
Since we now have t
he
lock-related classes available in our arsenal, many of our earlier
questions can now be addressed. Let's begin looking
at the issue of lock scope by modifying our
ScoreLabel class:
package javathreads.examples.ch03.example3;
...
public class ScoreLabel extends JLabel implements CharacterListener {
...
public void newCharacter(CharacterEvent ce) {
if (ce.source == generator) {
try {
scoreLock.lock( );
// Previous character not typed correctly: 1-point penalty
if (char2type != -1) {
score--;
setScore( );
}
char2type = ce.character;
} finally {
scoreLock.unlock( );
}
}
// If character is extraneous: 1-point penalty
// If character does not match: 1-point penalty
else {
try {
scoreLock.lock( );
if (char2type != ce.character) {
score--;
} else {
score++;
char2type = -1;
}
setScore( );
} finally {
scoreLock.unlock( );
}
}
}
}
Since the lock() and unlock()
method calls are explicit, we can move them anywhere, establishing
any lock scope, from a single line of code to a scope that spans
multiple methods and objects. By providing the means of specifying
the scope of the lock, we can now move time-consuming and threadsafe
code outside of the lock scope. And we can now lock at a scope that
is specific to the program design instead of the object layout. In
this example, we moved the source check outside of the lock, and we
also split the lock in two, one for each of the conditions.
3.5.1 Synchronized Blocks
It is possible for the
synchronized
keyword
to lock a block of code within a method. It is also possible for the
synchronized keyword to specify the object whose
lock is grabbed instead of using the lock of the object that contains
the method. Much of what we accomplish with the
Lock interface can still be done with the
synchronized keyword. It is possible to lock at a
scope that is smaller than a method, and it is possible to create an
object just so that it can be used as an synchronization object. We
can implement our last example just by using the
synchronized keyword:
package javathreads.examples.ch03.example4;
...
public class ScoreLabel extends JLabel implements CharacterListener {
...
// Definition for score lock deleted
...
public synchronized void resetGenerator(CharacterSource newGenerator) {
...
}
public synchronized void resetTypist(CharacterSource newTypist) {
...
}
public synchronized void resetScore( ) {
...
}
private synchronized void setScore( ) {
...
}
public void newCharacter(CharacterEvent ce) {
// Previous character not typed correctly: 1-point penalty
if (ce.source == generator) {
synchronized(this) {
if (char2type != -1) {
score--;
setScore( );
}
char2type = ce.character;
}
}
// If character is extraneous: 1-point penalty
// If character does not match: 1-point penalty
else {
synchronized(this) {
if (char2type != ce.character) {
score--;
} else {
score++;
char2type = -1;
}
setScore( );
}
}
}
}
This syntax of the synchronized keyword requires
an object whose lock is obtained. This is similar to our
scoreLock object in the previous example. For this
example, we are locking with the same object that was used for the
synchronization of the method: the this object.
Using this syntax, we can now lock individual lines of code instead
of the whole method. We can also share data across multiple objects
by locking on other objects instead, such as the data object to be shared.
|
It is possible to use only the
synchronized block mechanism even
when we need to synchronize the whole method. For clarity in this
book, we synchronize the whole method with the synchronized method
mechanism and use the synchronized block mechanism otherwise. It is
the programmer's personal preference to decide when
to synchronize on a block of code and when to synchronize the whole
method — with the caveat that it's always
better to establish as small a lock scope as possible.
|
|