站内搜索: 请输入搜索关键词
当前页面: 图书首页 > Java Threads, Third Edition

3.1 The Synchronized Keyword - Java Threads, Third Edition

Previous Section  < Day Day Up >  Next Section

3.1 The Synchronized Keyword

Let's revisit our AnimatedDisplayCanvas class from the previous chapter:

package javathreads.examples.ch02.example7;

    private volatile boolean done = false;

    private int curX = 0;



public class AnimatedCharacterDisplayCanvas extends CharacterDisplayCanvas 

                 implements CharacterListener, Runnable {

    ...

    public synchronized void newCharacter(CharacterEvent ce) {

        curX = 0;

        tmpChar[0] = (char) ce.character;

        repaint( );

    }



    protected synchronized void paintComponent(Graphics gc) {

        Dimension d = getSize( );

        gc.clearRect(0, 0, d.width, d.height);

        if (tmpChar[0] == 0)

            return;

        int charWidth = fm.charWidth(tmpChar[0]);

        gc.drawChars(tmpChar, 0, 1,

                     curX++, fontHeight);

    }



    public void run( ) {

        while (!done) {

            repaint( );

            try {

                Thread.sleep(100);

            } catch (InterruptedException ie) {

                return;

            }

        }

    }



    public void setDone(boolean b) {

        done = b;

    }

}

This example has multiple threads. The most obvious is the one that we created and which executes the run() method. That thread is specifically created to wake up every 0.1 seconds to send a repaint request to the system. To fulfill the repaint request, the system梐t a later time and in a different thread (the event-dispatching thread, to be precise)梒alls the paintComponent() method to adjust and redraw the canvas. This constant adjustment and redrawing is what is seen as animation by the user.

There is no race condition between these threads since no data in this object is shared between them. However, as we mentioned at the end of the last chapter, other threads invoke methods of this object. For example, the newCharacter() method is called from the random character-generating thread (a character source) whenever the character to be typed changes.

In this case, there is a race condition. The thread that calls the newCharacter() method is accessing the same data as the thread that calls the paintComponent( ) method. The random character-generating thread may change the character while the event-dispatching thread is using it. Both threads are also changing the X location that specifies where the character is to be drawn.

A race condition exists because the paintComponent() and newCharacter() methods are not atomic. It is possible for the newCharacter() method to change the values of the tmpChar and curX variables while the paintComponent() method is using them. Or for the newCharacter() and paintComponent() methods to leave the curX variable in a state that depends on which individual instructions of the two threads are executed first. We examine race conditions in more detail later; for now, we just have to understand that race conditions can generate different results, including unexpected results, that are dependent on execution order.

Definition: Atomic

The term atomic is related to the atom, once considered the smallest possible unit of matter, unable to be broken into separate parts. When computer code is considered atomic, it cannot be interrupted during its execution. This can either be accomplished in hardware or simulated in software. Generally, atomic instructions are provided in hardware and are used to implement atomic methods in software.

In our case, we define atomic code as code that can't be found in an intermediate state. In our animated canvas example, if the acts of "resetting the variable" and "redrawing one frame of the animation" were atomic, it would not be possible to set the variable at the very moment that the character is being animated. The animation thread also couldn't find the variables in an intermediate state.


The Java specification provides certain mechanisms that deal specifically with this problem. The Java language provides the synchronized keyword; in comparison with other threading systems, this keyword allows the programmer access to a resource that is very similar to a mutex lock. For our purposes, it simply prevents two or more threads from calling the methods of the same object at the same time.

Definition: Mutex Lock

A mutex lock is also known as a mutually exclusive lock. This type of lock is provided by many threading systems as a means of synchronization. Only one thread can grab a mutex at a time: if two threads try to grab a mutex, only one succeeds. The other thread has to wait until the first thread releases the lock before it can grab the lock and continue operation.

In Java, every object has an associated lock. When a method is declared synchronized, the executing thread must grab the lock associated with the object before it can continue. Upon completion of the method, the lock is automatically released.


By declaring the newCharacter() and paintComponent() methods synchronized, we eliminate the race condition. If one thread wants to call one of these methods while another thread is already executing one of them, the second thread must wait: the first thread gets to complete execution of its method before the second thread can execute its method. Since only one thread gets to call either method at a time, only one thread at a time accesses the data.

Under the covers, the concept of synchronization is simple: when a method is declared synchronized, the thread that wants to execute the method must acquire a token, which we call a lock. Once the method has acquired (or checked out or grabbed) this lock, it executes the method and releases (or returns) the lock. No matter how the method returns梚ncluding via an exception梩he lock is released. There is only one lock per object, so if two separate threads try to call synchronized methods of the same object, only one can execute the method immediately; the other has to wait until the first thread releases the lock before it can execute the method.

    Previous Section  < Day Day Up >  Next Section