Javasynchronization粗解
synchronized
关键字可以用来修饰代码块,或者修饰方法。
The synchronized
statement (§14.19) computes a reference to an object; it then attempts to perform a lock action on that object"s monitor and does not proceed further until the lock action has successfully completed. After the lock action has been performed, the body of the synchronized
statement is executed. If execution of the body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.
A synchronized
statement acquires a mutual-exclusion lock (synchronized是互斥锁,同时只能被一个线程拥有) on behalf of the executing thread, executes a block, then releases the lock. While the executing thread owns the lock, no other thread may acquire the lock.
SynchronizedStatement:
synchronized
(
Expression
)
Block
The type of Expression must be a reference type, or a compile-time error occurs.(Expression必须是引用类型,否则就会有编译错误)
A synchronized
statement is executed by first evaluating the Expression. Then:
If evaluation of the Expression completes abruptly for some reason, then the
synchronized
statement completes abruptly for the same reason.Otherwise, if the value of the Expression is
null
, aNullPointerException
is thrown.Otherwise, let the non-
null
value of the Expression beV
. The executing thread locks the monitor associated withV
. Then the Block is executed, and then there is a choice:If execution of the Block completes normally, then the monitor is unlocked and the
synchronized
statement completes normally.If execution of the Block completes abruptly for any reason, then the monitor is unlocked and the
synchronized
statement completes abruptly for the same reason.
The locks acquired by synchronized
statements are the same as the locks that are acquired implicitly by synchronized
methods (§8.4.3.6). A single thread may acquire a lock more than once.
Acquiring the lock associated with an object does not in itself prevent other threads from accessing fields of the object or invoking un-synchronized
methods on the object. Other threads can also use synchronized
methods or the synchronized
statement in a conventional manner to achieve mutual exclusion.
Example 14.19-1. The synchronized
Statement
class Test {public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
}
This program produces the output:
made it!
Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.
A synchronized
method (§8.4.3.6) automatically performs a lock action when it is invoked; its body is not executed until the lock action has successfully completed. If the method is an instance method, it locks the monitor associated with the instance for which it was invoked (that is, the object that will be known as this
during execution of the body of the method). If the method is static
, it locks the monitor associated with the Class
object that represents the class in which the method is defined. If execution of the method"s body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.
A synchronized
method acquires a monitor (§17.1) before it executes.
For a class (static
) method, the monitor associated with the Class
object for the method"s class is used.
For an instance method, the monitor associated with this
(the object for which the method was invoked) is used.
Example 8.4.3.6-1. synchronized
Monitors
These are the same monitors that can be used by the synchronized
statement (§14.19).
Thus, the code:
class Test {int count;
synchronized void bump() {
count++;
}
static int classCount;
static synchronized void classBump() {
classCount++;
}
}
has exactly the same effect as:
class BumpTest {int count;
void bump() {
synchronized (this) { count++; }
}
static int classCount;
static void classBump() {
try {
synchronized (Class.forName("BumpTest")) {
classCount++;
}
} catch (ClassNotFoundException e) {}
}
}
Example 8.4.3.6-2. synchronized
Methods
public class Box {private Object boxContents;
public synchronized Object get() {
Object contents = boxContents;
boxContents = null;
return contents;
}
public synchronized boolean put(Object contents) {
if (boxContents != null) return false;
boxContents = contents;
return true;
}
}
This program defines a class which is designed for concurrent use. Each instance of the class Box
has an instance variable boxContents
that can hold a reference to any object. You can put an object in a Box
by invoking put
, which returns false
if the box is already full. You can get something out of a Box
by invoking get
, which returns a null reference if the box is empty.
If put
and get
were not synchronized
, and two threads were executing methods for the same instance of Box
at the same time, then the code could misbehave. It might, for example, lose track of an object because two invocations to put occurred at the same time.
附 volatile
Fields
The Java programming language allows threads to access shared variables (§17.1). As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.(java允许线程之间共享变量。但是作为一项规则,为了确保共享变量得到一致和可靠的更新,线程应该确保它通过获得一个锁来独占这些变量,通常,该锁强制这些共享变量相互排斥。)
The Java programming language provides a second mechanism, volatile
fields, that is more convenient than locking for some purposes.
A field may be declared volatile
, in which case the Java Memory Model ensures that all threads see a consistent value for the variable (§17.4).
It is a compile-time error if a final
variable is also declared volatile
.(变量不能同时被final和volatile修饰)
Example 8.3.1.4-1. volatile
Fields
If, in the following example, one thread repeatedly calls the method one
(but no more than Integer.MAX_VALUE
times in all), and another thread repeatedly calls the method two
:
class Test {static int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
then method two
could occasionally print a value for j
that is greater than the value of i
, because the example includes no synchronization and, under the rules explained in §17.4, the shared values of i
and j
might be updated out of order.
One way to prevent this out-or-order behavior would be to declare methods one
and two
to be synchronized
(§8.4.3.6):
class Test {static int i = 0, j = 0;
static synchronized void one() { i++; j++; }
static synchronized void two() {
System.out.println("i=" + i + " j=" + j);
}
}
This prevents method one
and method two
from being executed concurrently, and furthermore guarantees that the shared values of i
and j
are both updated before method one
returns. Therefore method two
never observes a value for j
greater than that for i
; indeed, it always observes the same value for i
and j
.
Another approach would be to declare i
and j
to be volatile
:
class Test {static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
This allows method one
and method two
to be executed concurrently, but guarantees that accesses to the shared values for i
and j
occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j
is never greater than that for i
, because each update to i
must be reflected in the shared value for i
before the update to j
occurs. It is possible, however, that any given invocation of method two
might observe a value for j
that is much greater than the value observed for i
, because method one
might be executed many times between the moment when method two
fetches the value of i
and the moment when method two fetches the value of j
.
See §17.4 for more discussion and examples.
以上是 Javasynchronization粗解 的全部内容, 来源链接: utcz.com/z/514360.html