An implicit assumption in everything we’ve done so far is that a single program is modifying our data structures. In Java, one can have the effect of multiple programs modifying an object, due to the existence of threads
Although the language used to describe threads suggests that their purpose is to allow several things to happen simultaneously, this is a somewhat misleading impression. Even the smallest Java application running on Sun ’s JDK platform, for example, has five threads, and that’s only if the application has not created any itself, and even if the machine on which the program runs consists of a single processor (which can only execute one instruction at a time). The four additional “system threads” perform a number of tasks (such as “finalizing” objects that are no longer reachable by the program) that are logically independent of the rest of the program.Their actions could usefully occur at any time relative to the rest of the program.Sun’s Java run time system, in other words, is using threads as a organizational tool for its system.
Threads abound in Java programs that use graphical user interfaces(GUIs). One thread draws or redraws the screen. Another responds to e vents such as the clicking of a mouse button at some point on the screen. These are related,but largely independent activities: objects must be redrawn, for example, whenever a window becomes invisible and uncovers them, which happens independently of any calculations the program is doing.Threads violate our implicit assumption that a single program operates on our data, so that even an otherwise perfectly implemented data structure, with all of its instance variables private, can become corrupted in rather bizarre ways. The existence of multiple threads operating on the same data objects also raises the general problem of how these threads are to communicate with each other in an orderly fashion.
## 10.1 Synchronized Data Structures
Consider the `ArrayList` implementation from §4.1. In the method `ensureCapacity`, we find
```java
publicvoidensureCapacity(intN){
if(N<=data.length)
return;
Object[]newData=newObject[N];
System.arraycopy(data,0,newData,0,count);
data=newData;
}
```
```java
publicObjectset(intk,Objectx){
check(k,count);
Objectold=data[k];
data[k]=x;
returnold;
}
```
Suppose one program executes `ensureCapacity` while another is executing set on the same `ArrayList` object. We could see the following interleaving of their actions:
```java
/* Program 1 executes: */newData=newObject[N];
/* Program 1 executes: */System.arraycopy(data,0,newData,0,count);
/* Program 2 executes: */data[k]=x;
/* Program 1 executes: */data=newData;
```
Thus, we lose the value that Program 2 set, because it puts this value into the old value of `data` after `data’s` contents have been copied to the new, expanded array. To solve th e simple problem presented by `ArrayList`, threads can arrange to access any particular `ArrayList` in mutual exclusion—that is, in such a way that only one thread at a time operates on the object. Java’s `synchronized` statement provide mutual exclusion, allowing us to produce `synchronized` (or thread-safe) data structures. Here is part of an example, showing both the use of the `synchronized` method modifier and equivalent use of the synchronized statement:
```java
publicclassSyncArrayList<T>extendsArrayList<T>{
...
publicvoidensureCapacity(intn){
synchronized(this){
super.ensureCapacity(n);
}
}
publicsynchronizedTset(intk,Tx){
returnsuper.set(k,x);
}
...
}
```
The process of providing such wrapper functions for all methods of a List is sufficiently tedious that the standard Java library class `java.util.Collections` provides the following method:
```java
/** A synchronized (thread-safe) view of the list L, in which only
* one thread at a time executes any method. To be effective,
* (a) there should be no subsequent direct use of L,
* and (b) the returned List must be synchronized upon
* during any iteration, as in
*
* List aList = Collections.synchronizedList(new ArrayList());
* ...
* synchronized(aList) {
* for (Iterator i = aList.iterator(); i.hasNext(); )
Unfortunately, there is a time cost associated with synchronizing on every operation, which is why the Java library designers decided that `Collection` and most of its subtypes would not be synchronized. On the other hand, `StringBuffers` and `Vectors` are synchronized, and cannot be corrupted by simultaneous use.
## 10.2 Monitors and Orderly Communication
The objects returned by the `synchronizedList` method are examples of the simplest kind of monitor. This term refers to an object (or type of object) that controls (“monitors”) concurrent access to some data structure so as to make it work correctly. One function of a monitor is to provide mutually exclusive access to the operations of the data structure, where needed. Another is to arrange for synchronization between threads—so that one thread can wait until an object is “ready” to provide it with some service.
Monitors are exemplified by one of the classic examples: the shared buffer or mailbox. A simple version of its public specification looks like this:
```java
/** A container for a single message (an arbitrary Object). At any
* time, a SmallMailbox is either empty (containing no message) or
* full (containing one message). */
publicclassSmallMailbox{
/** When THIS is empty, set its current message to MESSAGE, making
* it full. */
publicsynchronizedvoiddeposit(Objectmessage)
throwsInterruptedException{...}
/** When THIS is full, empty it and return its current message. */
publicsynchronizedObjectreceive()
throwsInterruptedException{...}
}
```
Since the specifications suggest that either method might have to wait for a new message to be deposited or an old one to be received, we specify both as possibly throwing an `InterruptedException`, which is the standard Java way to indicate that while we were waiting, some other thread interrupted us.
The `SmallMailbox` specification illustrates the features of a typical monitor:
- None of the modifiable state variables (i.e., fields) are exposed.
- Accesses from separate threads that make any reference to modifiable state are mutually excluded; only one thread at a time holds a lock on a `SmallMailbox` object.
- A thread may relinquish a lock temporarily and await notification of some change. But changes in the ownership of a lock occur only at well-defined points in the program.
The internal representation is simple:
```java
privateObjectmessage;
privatebooleanamFull;
```
The implementations make use of the primitive Java features for “waiting until notified:”
```java
publicsynchronizedvoiddeposit(Objectmessage)
throwsInterruptedException{
while(amFull)
wait();// Same as this.wait ();
this.message=message;
this.amFull=true;
notifyAll();// Same as this.notifyAll ()
}
publicsynchronizedObjectreceive()
throwsInterruptedException{
while(!amFull)
wait();
amFull=false;
notifyAll();
returnmessage;
}
```
The methods of `SmallMailbox` allow other threads in only at carefully controlled points: the calls to wait. For example, the loop in `deposit` means “If there is still old unreceived mail, wait until some other thread to receives it and wakes me up again (with `notifyAll`) and I have managed to lock this mailbox again.” From the point of view of a thread that is executing `deposit` or `receive`, each call to `wait` has th e effect of causing some change to the in stance variables of `this`—some change, that is, that could be effected by other calls `deposit` or `receive`.
As long as the threads of a program are careful to protect all their data in monitors in this fashion, they will avoid the sorts of bizarre interaction described at the beginning of §10.1. Of course, there is no such thing as a free lunch; the use of locking can lead to the situation known as deadlock in which two or more threads wait for each other indefinitely, as in this artificial example:
Since neither thread sends anything before trying to receive a message from its box, both threads wait for each other (the problem could be solved by having one of the two threads reverse the order in which it receives and deposits).
## 10.3 Message Passing
Monitors provide a disciplined way for multiple threads to access data without stumbling over each other. Lurking behind the concept of monitor is a simple idea:
> Thinking about multiple programs executing simultaneously is hard, so don’t do it! Instead, write a bunch of one-thread programs, and have them exchange data with each other
In the case of general monitors, “exchanging data” means setting variables that each can see. If we take the idea further, we can in stead define “exchanging data” as “reading input and writing output.” We get a concurrent programming discipline called message passing.
In the message-passing world, threads are independent sequential programs than send each other messages. They read and write messages using methods that correspond to `read` on Java `Readers`, or `print` on Java `PrintStreams`. As a result, one thread is affected by another only when it bothers to “read its messages.”
We can get the effect of message passing by writing our threads to perform all interaction with each other by means of mailboxes. That is, the threads share some set of mailboxes, but share no other modifiable objects or variables (unmodifiable objects, like Strings, are fine to share)
## Exercises
**10.1** Give a possible implementation for the `Collections.synchronizedList` static method in §10.1.
An implicit assumption in everyth ing we’ve done so far is that a single program ismodifying our data structures. In Java, one can have the effect of multiple programsmodifying an object, due to the existence of threads
Although the language used to describe threads suggests that their purpose is to allow several things to happen simultaneously, this is a somewhat misleading impression. Even the smallest Java application running on Sun ’s JDK platform, for example, has five threads, and that’s only if the application has not created any itself, and even if the machine on which the program runs consists of a single processor (which can only execute one instruction at a time). The four additional “system threads” perform a number of tasks (such as “finalizing” objects that are no longer reachable by the program) that are logically independent of the rest of the program.Their actions could usefully occur at any time relative to the rest of the program.Sun’s Java run time system, in other words, is using threads as a organizational tool for its system.
Threads abound in Java programs that use graphical user interfaces(GUIs). One thread draws or redraws the screen. Another responds to e vents such as the clicking of a mouse button at some point on the screen. These are related,but largely independent activities: objects must be redrawn, for example, whenever a window becomes invisible and uncovers them, which happens independently of any calculations the program is doing.Threads violate our implicit assumption that a single program operates on our data, so that even an otherwise perfectly implemented data structure, with all of its instance variables private, can become corrupted in rather bizarre ways. The existence of multiple threads operating on the same data objects also raises the general problem of how these threads are to communicate with each other in an orderly fashion.
Suppose one program executes `ensureCapacity` while another is executing set on the same `ArrayList` object. We could see the following interleaving of their actions:
/* Program 1 executes: */System.arraycopy(data,0,newData,0,count);
...
...
@@ -40,10 +44,9 @@ Suppose one program executes `ensureCapacity` while another is executing set on
/* Program 1 executes: */data=newData;
```
Thus, we lose the value that Program 2 set, because it puts this value into the old value of `data` after `data’s` contents have been copied to the new expanded array. To solve the simple problem presented by `ArrayList`, threads can arrange to access any particular `ArrayList` in mutual exclusion—that is, in such a way that only one thread at a time operates on the object. Java’s `synchronized` statement provide mutual exclusion, allowing us to produce `synchronized` (or thread-safe) data structures. Here is part of an example, showing both the use of the `synchronized` method modifier and equivalent use of the synchronized statement:
Thus, we lose the value that Program 2 set, because it puts this value into th e old value of `data` after `data’s` contents h ave been copied to the new, expanded array. To solve th e simple problem presented by `ArrayList`, threads can arrange to access any particular `ArrayList` in mutual exclusion—that is, in such a way that only one thread at a time operates on the object. Java’s `synchronized` statement provide mutual exclusion, allowing us to produce `synchronized` (or thread-safe) data structures. Here is part of an example, showing both the use of the `synchronized` method modifier and equivalent use of the synchronized statement:
@@ -59,8 +62,7 @@ public class SyncArrayList<T> extends ArrayList<T> {
}
```
The process of providing such wrapper functions for all methods of a List is sufficiently tedious that the standard Java library class `java.util.Collections` provides the following method:
Unfortunately, there is a time cost associated with synchronizing on every operation, which is why the Java library designers decided that `Collection` and most of its subtypes would not be synchronized. On the other hand, `StringBuffers` and `Vectors` are synchronized, and cannot be corrupted by simultaneous use.
The objects returned by the `synchronizedList` method are examples of the simplest kind of monitor. This term refers to an object (or type of object) that controls (“monitors”) concurrent access to some data structure so as to make it work correctly. One function of a monitor is to provide mutually exclusive access to the operations of the data structure, where needed. Another is to arrange for synchronization between threads—so that one thread can wait until an object is “ready” to provide it with some service.
Monitors are exemplified by one of the classic examples: the shared buffer or mailbox. A simple version of its public specification looks like this:
监视器可以用一个典型的例子来说明:共享缓冲区或邮箱。其公共规范的一个简单版本如下:
```java
/** A container for a single message (an arbitrary Object). At any
* time, a SmallMailbox is either empty (containing no message) or
...
...
@@ -101,24 +107,21 @@ public class SmallMailbox {
}
```
Since the specifications suggest that either method might have to wait for a new message to be deposited or an old one to be received, we specify both as possibly throwing an `InterruptedException`, which is the standard Java way to indicate that while we were waiting, some other thread interrupted us.
- None of the modifiable state variables (i.e., fields) are exposed.
- Accesses from separate th reads that make any reference to modifiable state are mutually excluded; only one thread at a time holds a lock on a `SmallMailbox` object.
- A thread may relinquish a lock temporarily and await notification of some change. But changes in the ownership of a lock occur only at well-defined points in the program.
The internal representation is simple:
内部表示很简单:
```java
privateObjectmessage;
privatebooleanamFull;
```
The implementations make use of the primitive Java features for “waiting until notified:”
这些实现使用了基本的Java特性来“等待直到被通知”:
```java
publicsynchronizedvoiddeposit(Objectmessage)
...
...
@@ -141,8 +144,14 @@ public synchronized Object receive ()
The methods of `SmallMailbox` allow other threads in only at carefully controlled points: the calls to wait. For example, the loop in `deposit` means “If there is still old unreceived mail, wait until some other thread to receives it and wakes me up again (with `notifyAll`) and I have managed to lock this mailbox again.” From the point of view of a thread that is executing `deposit` or `receive`, each call to `wait` has th e effect of causing some change to the in stance variables of `this`—some change, that is, that could be effected by other calls `deposit` or `receive`.
As long as the threads of a program are careful to protect all their data in monitors in this fashion, they will avoid the sorts of bizarre interaction described at the beginning of §10.1. Of course, there is no such thing as a free lunch; the use of locking can lead to the situation known as deadlock in which two or more threads wait for each other indefinitely, as in this artificial example:
Since neither thread sends anything before trying to receive a message from its box, both threads wait for each other (the problem could be solved by having one of the two threads reverse the order in which it receives and deposits).
Monitors provide a disciplined way for multiple threads to access data without stumbling over each other. Lurking behind the concept of monitor is a simple idea:
> Thinking about multiple programs executing simultaneously is hard, so don’t do it! Instead, write a bunch of one-thread programs, and have them exchange data with each other
> 考虑到同时执行多个程序是很困难的,所以不要这样做!相反,编写一组单线程程序,让它们彼此交换数据
In the case of general monitors, “exchanging data” means setting variables that each can see. If we take the idea further, we can in stead define “exchanging data” as “reading input and writing output.” We get a concurrent programming discipline called message passing.
In the message-passing world, threads are independent sequential programs than send each other messages. They read and write messages using methods that correspond to `read` on Java `Readers`, or `print` on Java `PrintStreams`. As a result, one th read is affected by another only when it bothers to “read its messages.”
We can get th e effect of message passing by writing our threads to perform all interaction with each other by means of mailboxes. That is, the threads share some set of mailboxes, but share no other modifiable objects or variables (unmodifiable objects, like Strings, are fine to share)