Concurrency
Processes and Threads
Thread Object
Defining and Starting a Thread
Pausing Execution with sleep
Two overloaded versions of sleep are provided:
- one that specifies the sleep time to the millisecond
- one that specifies the sleep time to the nanosecond.
However, these sleep times are NOT guaranteed to be precise, because they are limited by the facilities provided by the underlying OS. Also, the sleep period can be terminated by interrupts
public static void main(String args[])
throws InterruptedException {
Thread.sleep(4000);
}
Notice that main declares that it throws InterruptedException.
Interrupts
if( Thread.interruped() ){
...
handle interrupt event
...
}
The Interrupt Status Flag
The interrupt mechanism is implemented using an internal flag known as the interrupt status.
Joins
The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,
t.join();
Synchhronization
Threads communicate primarily by sharing access to fields and the objects reference fields refer to. This form of communication is extremely efficient, but makes two kinds of errors possible: thread interference and memory consistency errors.
Thread Interference
class Counter {
private int c = 0;
public void increment() {
c++;
}
public void decrement() {
c--;
}
public int value() {
return c;
}
}
Memory Consistency Errors
The key to avoiding memory consistency errors is understanding the happens-before relationship. This relationship is simply a guarantee that memory writes by one specific statement are visible to another specific statement. To see this, consider the following example. Suppose a simple int field is defined and initialized:
int counter = 0;
The counter field is shared between two threads, A and B. Suppose thread A increments counter:
counter++;
Then, shortly afterwards, thread B prints out counter:
System.out.println(counter);
Synchronized Methods
Note that constructors cannot be synchronized — using the synchronized keyword with a constructor is a syntax error. Synchronizing constructors doesn't make sense, because only the thread that creates an object should have access to it while it is being constructed.
You might be tempted to add the following line to your constructor:
instances.add(this);
But then other threads can use instances to access the object before construction of the object is complete.
Intrinsic Locks and Synchronization
Synchronization is built around an internal entity known as the intrinsic lock or monitor lock.
Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.
Locks In Synchronized Methods
When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.
Static Synchronized function -> acquire lock of Class Object of assciated class. Thus access to class's static field is controlled by a lock that's distinct from the lock of any instance of the class.
sychronized(this){
//acquire lock of object
}
sychronized(CLASS_A){
//acquire lock of CLASS_A,
//all object of CLASS_A will blocked before release
}
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables reentrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.
Synchronized method may call Another Synchronized method, Cause it's within same thread. it's ok and safe to reacquire lock by same thread.
Atomic Access
Interview Questions:
What's volatile key word? When to use it?
volatile:
Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable. This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.
Some of the classes in the java.util.concurrent
package provide atomic methods that do not rely on synchronization. We'll discuss them in the section on High Level Concurrency Objects.
Liveness
Deadlock
Starvation and Livelock
GuardedBlocks
public synchronized void guardedJoy() {
// This guard only loops once for each special event, which may not
// be the event we're waiting for.
while(!joy) {
try {
wait();
} catch (InterruptedException e) {
//wait can throw exception so need to surround with try catch inside
//test of joy
}
}
System.out.println("Joy and efficiency have been achieved!");
}
*Note: Always invoke wait inside a loop that tests for the condition being waited for. Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true.
Important: it's a Java idiom to make wait() inside loop, in case exception happens
Why is this version of guardedJoy synchronized? Suppose d is the object we're using to invoke wait. When a thread invokes d.wait, it must own the intrinsic lock for d — otherwise an error is thrown. Invoking wait inside a synchronized method is a simple way to acquire the intrinsic lock.
My Note:
wait will release lock of objec, so it have to occupy the lock first. that's why synchronized needed for function guardedJoy.
When wait is invoked, the thread releases the lock and suspends execution. At some future time, another thread will acquire the same lock and invoke Object.notifyAll, informing all threads waiting on that lock that something important has happened
public synchronized notifyJoy() {
joy = true;
notifyAll();
}
Some time after the second thread has released the lock, the first thread reacquires the lock and resumes by returning from the invocation of wait.
My Note:
Different with notify() & notifyAll():
notify() only wake up one thread in wait condition. notifyAll() will wake up all threads in wait.
Try not use notify unless you know exactly how your program goes.
e.g.
TA wait for A to be true, TB wait for B to be true. TA' set A true and call notify,
then B wake up, find B still false and wait() again. Thus, A and B goes to Liveness
Example
Immutable object
A Strategy for Defining Immutable Objects
-
Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
-
Make all fields final and private.
-
Don't allow subclasses to override methods. The simplest way to do this is to declare the class as final. A more sophisticated approach is to make the constructor private and construct instances in factory methods.
-
If the instance fields include references to mutable objects, don't allow those objects to be changed:
- Don't provide methods that modify the mutable objects.
- Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
Interview Questions:
1.
how to define Immutable Object1.1
how to disallow override
final public class ImmutableRGB {
// Values must be between 0 and 255.
final private int red;
final private int green;
final private int blue;
final private String name;
private void check(int red,
int green,
int blue) {
if (red < 0 || red > 255
|| green < 0 || green > 255
|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public ImmutableRGB(int red,
int green,
int blue,
String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public String getName() {
return name;
}
public ImmutableRGB invert() {
return new ImmutableRGB(255 - red,
255 - green,
255 - blue,
"Inverse of " + name);
}
}
网友评论