System, but they may not be reproduced for publication


part, how much CPU time a thread receives relative to the other active threads. In



Yüklə 83 Mb.
Pdf görüntüsü
səhifə50/82
tarix19.04.2023
ölçüsü83 Mb.
#106251
1   ...   46   47   48   49   50   51   52   53   ...   82
Java A Beginner’s Guide, Eighth Edition ( PDFDrive )


part, how much CPU time a thread receives relative to the other active threads. In
general, over a given period of time, low­priority threads receive little. High­priority
threads receive a lot. As you might expect, how much CPU time a thread receives has
profound impact on its execution characteristics and its interaction with other threads
currently executing in the system.
It is important to understand that factors other than a thread’s priority also affect how
much CPU time a thread receives. For example, if a high­priority thread is waiting on
some resource, perhaps for keyboard input, then it will be blocked, and a lower­priority
thread will run. However, when that high­priority thread gains access to the resource, it
can preempt the low­priority thread and resume execution. Another factor that affects
the scheduling of threads is the way the operating system implements multitasking.
(See “Ask the Expert” at the end of this section.) Thus, just because you give one thread
a high priority and another a low priority does not necessarily mean that one thread
will run faster or more often than the other. It’s just that the high­priority thread has
greater potential access to the CPU.
When a child thread is started, its priority setting is equal to that of its parent thread.
You can change a thread’s priority by calling setPriority( ), which is a member of
Thread. This is its general form:
final void setPriority(int level)
Here, level specifies the new priority setting for the calling thread. The value of level
must be within the range MIN_PRIORITY and MAX_PRIORITY. Currently, these
values are 1 and 10, respectively. To return a thread to default priority, specify
NORM_PRIORITY, which is currently 5. These priorities are defined as static final
variables within Thread.
You can obtain the current priority setting by calling the getPriority( ) method of
Thread, shown here:
final int getPriority( )
The following example demonstrates threads at different priorities. The threads are
created as instances of Priority. The run( ) method contains a loop that counts the
number of iterations. The loop stops when either the count reaches 10,000,000 or the
static variable stop is true. Initially, stop is set to false, but the first thread to finish


counting sets stop to true. This causes each other thread to terminate with its next
time slice. Each time through the loop the string in currentName is checked against
the name of the executing thread. If they don’t match, it means that a task­switch
occurred. Each time a task­switch happens, the name of the new thread is displayed,
and currentName is given the name of the new thread. Displaying each thread switch
allows you to watch (in a very imprecise way) when the threads gain access to the CPU.
After the threads stop, the number of iterations for each loop is displayed.


Here are the results of a sample run:
In this run, the high­priority thread got the greatest amount of the CPU time. Of course,
the exact output produced by this program will depend upon a number of factors,
including the speed of your CPU, the number of CPUs in your system, the operating
system you are using, and the number and nature of other tasks running in the system.
Thus, it is actually possible for the low­priority thread to get the most CPU time if the
circumstances are right.


Ask the Expert
Q
: Does the operating system’s implementation of multitasking affect
how much CPU time a thread receives?
A
: Aside from a thread’s priority setting, the most important factor affecting
thread execution is the way the operating system implements multitasking and
scheduling. Some operating systems use preemptive multitasking in which each
thread receives a time slice, at least occasionally. Other systems use nonpreemptive
scheduling in which one thread must yield execution before another thread will
execute. In nonpreemptive systems, it is easy for one thread to dominate,
preventing others from running.
SYNCHRONIZATION
When using multiple threads, it is sometimes necessary to coordinate the activities of
two or more. The process by which this is achieved is called synchronization. The most
common reason for synchronization is when two or more threads need access to a
shared resource that can be used by only one thread at a time. For example, when one
thread is writing to a file, a second thread must be prevented from doing so at the same
time. Another reason for synchronization is when one thread is waiting for an event
that is caused by another thread. In this case, there must be some means by which the
first thread is held in a suspended state until the event has occurred. Then, the waiting
thread must resume execution.
Key to synchronization in Java is the concept of the monitor, which controls access to
an object. A monitor works by implementing the concept of a lock. When an object is
locked by one thread, no other thread can gain access to the object. When the thread
exits, the object is unlocked and is available for use by another thread.
All objects in Java have a monitor. This feature is built into the Java language itself.
Thus, all objects can be synchronized. Synchronization is supported by the keyword
synchronized and a few well­defined methods that all objects have. Since
synchronization was designed into Java from the start, it is much easier to use than you
might first expect. In fact, for many programs, the synchronization of objects is almost
transparent.


There are two ways that you can synchronize your code. Both involve the use of the
synchronized keyword, and both are examined here.
USING SYNCHRONIZED METHODS
You can synchronize access to a method by modifying it with the synchronized
keyword. When that method is called, the calling thread enters the object’s monitor,
which then locks the object. While locked, no other thread can enter the method, or
enter any other synchronized method defined by the object’s class. When the thread
returns from the method, the monitor unlocks the object, allowing it to be used by the
next thread. Thus, synchronization is achieved with virtually no programming effort on
your part.
The following program demonstrates synchronization by controlling access to a method
called sumArray( ), which sums the elements of an integer array.


The output from the program is shown here. (The precise output may differ on your
computer.)


Let’s examine this program in detail. The program creates three classes. The first is
SumArray. It contains the method sumArray( ), which sums an integer array. The
second class is MyThread, which uses a static object of type SumArray to obtain the
sum of an integer array. This object is called sa and because it is static, there is only
one copy of it that is shared by all instances of MyThread. Finally, the class Sync
creates two threads and has each compute the sum of an integer array.
Inside sumArray( ), sleep( ) is called to purposely allow a task switch to occur, if one
can—but it can’t. Because sumArray( ) is synchronized, it can be used by only one
thread at a time. Thus, when the second child thread begins execution, it does not enter
sumArray( ) until after the first child thread is done with it. This ensures that the
correct result is produced.
To fully understand the effects of synchronized, try removing it from the declaration
of sumArray( ). After doing this, sumArray( ) is no longer synchronized, and any
number of threads may use it concurrently. The problem with this is that the running
total is stored in sum, which will be changed by each thread that calls sumArray( )
through the static object sa. Thus, when two threads call sa.sumArray( ) at the same
time, incorrect results are produced because sum reflects the summation of both
threads, mixed together. For example, here is sample output from the program after
synchronized has been removed from sumArray( )’s declaration. (The precise
output may differ on your computer.)


As the output shows, both child threads are calling sa.sumArray( ) concurrently, and
the value of sum is corrupted. Before moving on, let’s review the key points of a
synchronized method:

A synchronized method is created by preceding its declaration with synchronized.

For any given object, once a synchronized method has been called, the object is
locked and no synchronized methods on the same object can be used by another thread
of execution.

Other threads trying to call an in­use synchronized object will enter a wait state until
the object is unlocked.

When a thread leaves the synchronized method, the object is unlocked.
THE SYNCHRONIZED STATEMENT
Although creating synchronized methods within classes that you create is an easy and
effective means of achieving synchronization, it will not work in all cases. For example,
you might want to synchronize access to some method that is not modified by
synchronized. This can occur because you want to use a class that was not created by
you but by a third party, and you do not have access to the source code. Thus, it is not
possible for you to add synchronized to the appropriate methods within the class.
How can access to an object of this class be synchronized? Fortunately, the solution to
this problem is quite easy: You simply put calls to the methods defined by this class


inside a synchronized block.
This is the general form of a synchronized block:
synchronized(objref) {
// statements to be synchronized
}
Here, objref is a reference to the object being synchronized. Once a synchronized block
has been entered, no other thread can call a synchronized method on the object
referred to by objref until the block has been exited.
For example, another way to synchronize calls to sumArray( ) is to call it from within
a synchronized block, as shown in this version of the program:


This version produces the same, correct output as the one shown earlier that uses a
synchronized method.
Ask the Expert
Q
: I have heard of something called the “concurrency utilities.” What
are these? Also, what is the Fork/Join Framework?


A
: The concurrency utilities, which are packaged in java.util.concurrent (and
its subpackages), support concurrent programming. Among several other items,
they offer synchronizers, thread pools, execution managers, and locks that expand
your control over thread execution. One of the most exciting features of the
concurrent API is the Fork/Join Framework.
The Fork/Join Framework supports what is often termed parallel programming.
This is the name commonly given to the techniques that take advantage of
computers that contain two or more processors (including multicore systems) by
subdividing a task into subtasks, with each subtask executing on its own processor.
As you can imagine, such an approach can lead to significantly higher throughput
and performance. The key advantage of the Fork/Join Framework is ease of use; it
streamlines the development of multithreaded code that automatically scales to
utilize the number of processors in a system. Thus, it facilitates the creation of
concurrent solutions to some common programming tasks, such as performing
operations on the elements of an array. The concurrency utilities in general, and
the Fork/Join Framework specifically, are features that you will want to explore
after you have become more experienced with multithreading.
THREAD COMMUNICATION USING NOTIFY( ),
WAIT( ), AND NOTIFYALL( )
Consider the following situation. A thread called T is executing inside a synchronized
method and needs access to a resource called R that is temporarily unavailable. What
should T do? If T enters some form of polling loop that waits for R, T ties up the object,
preventing other threads’ access to it. This is a less than optimal solution because it
Yüklə 83 Mb.

Dostları ilə paylaş:
1   ...   46   47   48   49   50   51   52   53   ...   82




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə