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, a NullPointerException is thrown.

  • Otherwise, let the non-null value of the Expression be V. The executing thread locks the monitor associated with V. 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

回到顶部