Synchronization Support in the Instruction Set
As mentioned earlier, the language provides two built-in ways to identify monitor regions in your programs: synchronized statements and synchronized methods. These two mechanisms, which implement the mutual exclusion aspect of synchronization, are supported by the Java virtual machine's instruction set.
Synchronized Statements
To create a synchronized statement, you use the synchronized keyword with an expression that evaluates to an object reference, as in the reverseOrder() method below:
// On CD-ROM in file threads/ex1/KitchenSync.java
class KitchenSync {
private int[] intArray = new int[10];
void reverseOrder() {
synchronized (this) {
int halfWay = intArray.length / 2;
for (int i = 0; i < halfWay; ++i) {
int upperIndex = intArray.length - 1 - i;
int save = intArray[upperIndex];
intArray[upperIndex] = intArray[i];
intArray[i] = save;
}
}
}
// ...
}
In the above case, the statements contained within the synchronized block will not be executed until a lock is acquired on the current object (this). If instead of a this reference, the expression yielded a reference to another object, the lock associated with that object would be acquired before the thread continued. If the expression yields a reference to an instance of class Class, the lock associated with the class is acquired.
Two opcodes, monitorenter and monitorexit, are used for synchronization blocks within methods. These opcodes are shown in the Table 20-1. Table 20-1. Monitors
Opcode Operand(s) Description
monitorenter none pop objectref, acquire the lock associated with objectref
monitorexit none pop objectref, release the lock associated with objectref
Table 20-1. Monitors
When monitorenter is encountered by the Java virtual machine, it acquires the lock for the object referred to by objectref on the stack. If the thread already owns the lock for that object, the count that is associated with the lock is incremented. Each time monitorexit is executed for the thread on the object, the count is decremented. When the count reaches zero, the monitor is released.
Here is the bytecode sequence generated by the reverseOrder() method of the KitchenSync class:
// First place the reference to the object to lock into local
// variable 1. This local variable will be used by both the
// monitorenter and monitorexit instructions.
0 aload_0 // Push local var 0 (the this reference)
1 astore_1 // Store into local var 1
// Now acquire the lock on the referenced object
// Push local var 1 (the this reference; the
2 aload_1 // object to lock)
// Pop reference, acquire the lock
3 monitorenter // on referenced object
// The code of the synchronized block begins here. A thread will not
// execute the next instruction, aload_0, until a lock has been
// successfully acquired on the this reference above.
4 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
5 getfield #4
8 arraylength // Pop array ref, push int array length
9 iconst_2 // Push constant int 2
10 idiv // Pop two ints, divide, push int result
// Pop int into local var 3:
11 istore_3 // int halfway = intArray.length/2;
// This is the start of the code for the for loop
12 iconst_0 // Push constant int 0
13 istore 4 // Pop into local var 2: int i = 0;
15 goto 65 // Jump to for loop condition check
// This is the start of the body of the for loop
18 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
19 getfield #4
22 arraylength // Pop array ref, push int array length
23 iconst_1 // Push constant int 1
24 isub // Pop two ints, subtract, push int result
25 iload 4 // Push int at local var 4 (i)
27 isub // Pop two ints, subtract, push int result
// Pop int into local var 5:
28 istore 5 // int upperindex = intArray.length - 1 - i;
30 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
31 getfield #4
34 iload 5 // Push int at local var 5 (upperIndex)
36 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop into local var 6:
37 istore 6 // int save = intArray[upperIndex];
39 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
40 getfield #4
43 iload 5 // Push int at local var 5 (upperIndex)
45 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
46 getfield #4
49 iload 4 // Push int at local var 4 (i)
51 iaload // Pop index, arrayref, push int at arrayref[index]
// Set arrayref[index] = value:
52 iastore // intArray[upperIndex] = intArray[i];
53 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
54 getfield #4
57 iload 4 // Push int at local var 4 (i)
59 iload 6 // Push int at local var 6 (save)
// Set arrayref[index] = value:
61 iastore // intArray[i] = save;
// The body of the for loop is now done, this instruction does
// the incrementing of the loop variable i
62 iinc 4 1 // Increment by 1 int at local var 4: ++i;
// This is the for loop condition check:
65 iload 4 // Push int at local var 4 (i)
67 iload_3 // Push int at local var 3 (halfway)
// Pop two ints, compare, jump if less than to
68 if_icmplt 18 // top of for loop body: for (; i < halfway;)
// The code of the synchronized block ends here
// The next two instructions unlock the object, making it available
// for other threads. The reference to the locked object was stored
// in local variable 1 above.
71 aload_1 // Push local var 1 (the this reference)
72 monitorexit // Pop ref, unlock object
73 return // return normally from method
// This is a catch clause for any exception thrown (and not caught
// from within the synchronized block. If an exception is thrown,
// the locked object is unlocked, making it available for other
// threads.
74 aload_1 // Push local var 1 (the this reference)
75 monitorexit // Pop ref, unlock object
76 athrow // rethrow the same exception
// The exception table shows the "catch all" clause covers the
// entire synchronized block, from just after the lock is acquired
// to just before the lock is released.
Exception table:
from to target type
4 71 74 any
Note that a catch clause ensures the locked object will be unlocked even if an exception is thrown from within the synchronized block. No matter how the synchronized block is exited, the object lock acquired when the thread entered the block will definitely be released.
Synchronized Methods
To synchronize an entire method, you just include the synchronized keyword as one of the method qualifiers, as in:
// On CD-ROM in file threads/ex1/HeatSync.java
class HeatSync {
private int[] intArray = new int[10];
synchronized void reverseOrder() {
int halfWay = intArray.length / 2;
for (int i = 0; i < halfWay; ++i) {
int upperIndex = intArray.length - 1 - i;
int save = intArray[upperIndex];
intArray[upperIndex] = intArray[i];
intArray[i] = save;
}
}
// ...
}
The Java virtual machine does not use any special opcodes to invoke or return from synchronized methods. When the virtual machine resolves the symbolic reference to a method, it determines whether the method is synchronized. If so, the virtual machine acquires a lock before invoking the method. For an instance method, the virtual machine acquires the lock associated with the object upon which the method is being invoked. For a class method, it acquires the lock associated with the class to which the method belongs (it locks a Class object). After a synchronized method completes, whether it completes by returning or by throwing an exception, the virtual machine releases the lock.
Here are the bytecodes that javac generates for HeatSync's reverseOrder() method:
0 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
1 getfield #4
4 arraylength // Pop array ref, push int array length
5 iconst_2 // Push constant int 2
6 idiv // Pop two ints, divide, push int result
// Pop int into local var 1:
7 istore_1 // int halfway = intArray.length/2;
// This is the start of the code for the for loop
8 iconst_0 // Push int constant 0
9 istore_2 // Pop into local var 2: int i = 0;
10 goto 54 // Jump to for loop condition check
// This is the start of the body of the for loop
13 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
14 getfield #4
17 arraylength // Pop array ref, push int array length
18 iconst_1 // Push constant int 1
19 isub // Pop two ints, subtract, push int result
20 iload_2 // Push int at local var 2 (i)
21 isub // Pop two ints, subtract, push int result
// Pop int into local var 3:
22 istore_3 // int upperindex = intArray.length - 1 - i;
23 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
24 getfield #4
27 iload_3 // Push int at local var 3 (upperIndex)
28 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop into local var 4:
29 istore 4 // int save = intArray[upperIndex];
31 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
32 getfield #4
35 iload_3 // Push int at local var 3 (upperIndex)
36 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
37 getfield #4
40 iload_2 // Push int at local var 2 (i)
41 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop value, index, and arrayref,
// Set arrayref[index] = value:
42 iastore // intArray[upperIndex] = intArray[i];
43 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
44 getfield #4
47 iload_2 // Push int at local var 2 (i)
48 iload 4 // Push int at local var 4 (save)
// Pop value, index, and arrayref,
// Set arrayref[index] = value:
50 iastore // intArray[i] = save;
// The body of the for loop is now done, this instruction does
// the incrementing of the loop variable i
51 iinc 2 1 // Increment by 1 int at local var 2: ++i;
// This is the for loop condition check:
54 iload_2 // Push int at local var 2 (i)
55 iload_1 // Push int at local var 1 (halfway)
// Pop two ints, compare, jump if less than to
56 if_icmplt 13 // top of for loop body: for (; i < halfway;)
59 return // return (void) from method
If you compare these bytecodes with the ones shown earlier for KitchenSync's reverseOrder() method, you will see that these bytecodes are in effect those of KitchenSync with the support for entering and exiting the monitor removed. Instructions at offset 0 through 56 of HeatSync's bytecodes correspond to instructions at offset 4 through 68 of KitchenSync's bytecodes. Because HeatSync's reverseOrder() method doesn't need a local variable slot to store the reference to the locked object, the local variable positions used by each method are different. The function of the instructions themselves, however, match up exactly.
Another difference between the two reverseOrder() methods is that the compiler doesn't create an exception table for HeatSync's reverseOrder() method. In HeatSync's case, an exception table isn't necessary. When this method is invoked, the Java virtual machine automatically acquires the lock on the this object. If this method completes abruptly, just as if it completes normally, the virtual machine will release the lock on the this object automatically.
A synchronized class (static) method operates in the same way as the synchronized instance method shown in the example above. The one difference is that instead of acquiring a lock on this (as there is no this in a class method), the thread must acquire a lock on the appropriate Class instance.
As mentioned earlier, the language provides two built-in ways to identify monitor regions in your programs: synchronized statements and synchronized methods. These two mechanisms, which implement the mutual exclusion aspect of synchronization, are supported by the Java virtual machine's instruction set.
Synchronized Statements
To create a synchronized statement, you use the synchronized keyword with an expression that evaluates to an object reference, as in the reverseOrder() method below:
// On CD-ROM in file threads/ex1/KitchenSync.java
class KitchenSync {
private int[] intArray = new int[10];
void reverseOrder() {
synchronized (this) {
int halfWay = intArray.length / 2;
for (int i = 0; i < halfWay; ++i) {
int upperIndex = intArray.length - 1 - i;
int save = intArray[upperIndex];
intArray[upperIndex] = intArray[i];
intArray[i] = save;
}
}
}
// ...
}
In the above case, the statements contained within the synchronized block will not be executed until a lock is acquired on the current object (this). If instead of a this reference, the expression yielded a reference to another object, the lock associated with that object would be acquired before the thread continued. If the expression yields a reference to an instance of class Class, the lock associated with the class is acquired.
Two opcodes, monitorenter and monitorexit, are used for synchronization blocks within methods. These opcodes are shown in the Table 20-1. Table 20-1. Monitors
Opcode Operand(s) Description
monitorenter none pop objectref, acquire the lock associated with objectref
monitorexit none pop objectref, release the lock associated with objectref
Table 20-1. Monitors
When monitorenter is encountered by the Java virtual machine, it acquires the lock for the object referred to by objectref on the stack. If the thread already owns the lock for that object, the count that is associated with the lock is incremented. Each time monitorexit is executed for the thread on the object, the count is decremented. When the count reaches zero, the monitor is released.
Here is the bytecode sequence generated by the reverseOrder() method of the KitchenSync class:
// First place the reference to the object to lock into local
// variable 1. This local variable will be used by both the
// monitorenter and monitorexit instructions.
0 aload_0 // Push local var 0 (the this reference)
1 astore_1 // Store into local var 1
// Now acquire the lock on the referenced object
// Push local var 1 (the this reference; the
2 aload_1 // object to lock)
// Pop reference, acquire the lock
3 monitorenter // on referenced object
// The code of the synchronized block begins here. A thread will not
// execute the next instruction, aload_0, until a lock has been
// successfully acquired on the this reference above.
4 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
5 getfield #4
8 arraylength // Pop array ref, push int array length
9 iconst_2 // Push constant int 2
10 idiv // Pop two ints, divide, push int result
// Pop int into local var 3:
11 istore_3 // int halfway = intArray.length/2;
// This is the start of the code for the for loop
12 iconst_0 // Push constant int 0
13 istore 4 // Pop into local var 2: int i = 0;
15 goto 65 // Jump to for loop condition check
// This is the start of the body of the for loop
18 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
19 getfield #4
22 arraylength // Pop array ref, push int array length
23 iconst_1 // Push constant int 1
24 isub // Pop two ints, subtract, push int result
25 iload 4 // Push int at local var 4 (i)
27 isub // Pop two ints, subtract, push int result
// Pop int into local var 5:
28 istore 5 // int upperindex = intArray.length - 1 - i;
30 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
31 getfield #4
34 iload 5 // Push int at local var 5 (upperIndex)
36 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop into local var 6:
37 istore 6 // int save = intArray[upperIndex];
39 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
40 getfield #4
43 iload 5 // Push int at local var 5 (upperIndex)
45 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
46 getfield #4
49 iload 4 // Push int at local var 4 (i)
51 iaload // Pop index, arrayref, push int at arrayref[index]
// Set arrayref[index] = value:
52 iastore // intArray[upperIndex] = intArray[i];
53 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
54 getfield #4
57 iload 4 // Push int at local var 4 (i)
59 iload 6 // Push int at local var 6 (save)
// Set arrayref[index] = value:
61 iastore // intArray[i] = save;
// The body of the for loop is now done, this instruction does
// the incrementing of the loop variable i
62 iinc 4 1 // Increment by 1 int at local var 4: ++i;
// This is the for loop condition check:
65 iload 4 // Push int at local var 4 (i)
67 iload_3 // Push int at local var 3 (halfway)
// Pop two ints, compare, jump if less than to
68 if_icmplt 18 // top of for loop body: for (; i < halfway;)
// The code of the synchronized block ends here
// The next two instructions unlock the object, making it available
// for other threads. The reference to the locked object was stored
// in local variable 1 above.
71 aload_1 // Push local var 1 (the this reference)
72 monitorexit // Pop ref, unlock object
73 return // return normally from method
// This is a catch clause for any exception thrown (and not caught
// from within the synchronized block. If an exception is thrown,
// the locked object is unlocked, making it available for other
// threads.
74 aload_1 // Push local var 1 (the this reference)
75 monitorexit // Pop ref, unlock object
76 athrow // rethrow the same exception
// The exception table shows the "catch all" clause covers the
// entire synchronized block, from just after the lock is acquired
// to just before the lock is released.
Exception table:
from to target type
4 71 74 any
Note that a catch clause ensures the locked object will be unlocked even if an exception is thrown from within the synchronized block. No matter how the synchronized block is exited, the object lock acquired when the thread entered the block will definitely be released.
Synchronized Methods
To synchronize an entire method, you just include the synchronized keyword as one of the method qualifiers, as in:
// On CD-ROM in file threads/ex1/HeatSync.java
class HeatSync {
private int[] intArray = new int[10];
synchronized void reverseOrder() {
int halfWay = intArray.length / 2;
for (int i = 0; i < halfWay; ++i) {
int upperIndex = intArray.length - 1 - i;
int save = intArray[upperIndex];
intArray[upperIndex] = intArray[i];
intArray[i] = save;
}
}
// ...
}
The Java virtual machine does not use any special opcodes to invoke or return from synchronized methods. When the virtual machine resolves the symbolic reference to a method, it determines whether the method is synchronized. If so, the virtual machine acquires a lock before invoking the method. For an instance method, the virtual machine acquires the lock associated with the object upon which the method is being invoked. For a class method, it acquires the lock associated with the class to which the method belongs (it locks a Class object). After a synchronized method completes, whether it completes by returning or by throwing an exception, the virtual machine releases the lock.
Here are the bytecodes that javac generates for HeatSync's reverseOrder() method:
0 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
1 getfield #4
4 arraylength // Pop array ref, push int array length
5 iconst_2 // Push constant int 2
6 idiv // Pop two ints, divide, push int result
// Pop int into local var 1:
7 istore_1 // int halfway = intArray.length/2;
// This is the start of the code for the for loop
8 iconst_0 // Push int constant 0
9 istore_2 // Pop into local var 2: int i = 0;
10 goto 54 // Jump to for loop condition check
// This is the start of the body of the for loop
13 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
14 getfield #4
17 arraylength // Pop array ref, push int array length
18 iconst_1 // Push constant int 1
19 isub // Pop two ints, subtract, push int result
20 iload_2 // Push int at local var 2 (i)
21 isub // Pop two ints, subtract, push int result
// Pop int into local var 3:
22 istore_3 // int upperindex = intArray.length - 1 - i;
23 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
24 getfield #4
27 iload_3 // Push int at local var 3 (upperIndex)
28 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop into local var 4:
29 istore 4 // int save = intArray[upperIndex];
31 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
32 getfield #4
35 iload_3 // Push int at local var 3 (upperIndex)
36 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
37 getfield #4
40 iload_2 // Push int at local var 2 (i)
41 iaload // Pop index, arrayref, push int at arrayref[index]
// Pop value, index, and arrayref,
// Set arrayref[index] = value:
42 iastore // intArray[upperIndex] = intArray[i];
43 aload_0 // Push the object ref at loc var 0 (the this ref)
// Pop object ref, push ref to instance variable
// intArray
44 getfield #4
47 iload_2 // Push int at local var 2 (i)
48 iload 4 // Push int at local var 4 (save)
// Pop value, index, and arrayref,
// Set arrayref[index] = value:
50 iastore // intArray[i] = save;
// The body of the for loop is now done, this instruction does
// the incrementing of the loop variable i
51 iinc 2 1 // Increment by 1 int at local var 2: ++i;
// This is the for loop condition check:
54 iload_2 // Push int at local var 2 (i)
55 iload_1 // Push int at local var 1 (halfway)
// Pop two ints, compare, jump if less than to
56 if_icmplt 13 // top of for loop body: for (; i < halfway;)
59 return // return (void) from method
If you compare these bytecodes with the ones shown earlier for KitchenSync's reverseOrder() method, you will see that these bytecodes are in effect those of KitchenSync with the support for entering and exiting the monitor removed. Instructions at offset 0 through 56 of HeatSync's bytecodes correspond to instructions at offset 4 through 68 of KitchenSync's bytecodes. Because HeatSync's reverseOrder() method doesn't need a local variable slot to store the reference to the locked object, the local variable positions used by each method are different. The function of the instructions themselves, however, match up exactly.
Another difference between the two reverseOrder() methods is that the compiler doesn't create an exception table for HeatSync's reverseOrder() method. In HeatSync's case, an exception table isn't necessary. When this method is invoked, the Java virtual machine automatically acquires the lock on the this object. If this method completes abruptly, just as if it completes normally, the virtual machine will release the lock on the this object automatically.
A synchronized class (static) method operates in the same way as the synchronized instance method shown in the example above. The one difference is that instead of acquiring a lock on this (as there is no this in a class method), the thread must acquire a lock on the appropriate Class instance.
相关推荐
Synchronization Support in the Instruction Set Synchronized Statements Synchronized Methods Coordination Support in Class Object On the CD-ROM The Resources Page Appendix A. Instructions by ...
latest results in the field of synchronization for OFDMA systems, with tutorial objectives foremost. After quantifying the effects of synchronization errors on the system performance, we review some ...
Adaptive pinning control of cluster synchronization in complex networks with Lurie-type nonlinear dynamics
Hardware support is often employed to provide synchronization primitives, like test-and-set, swap, and compare-and-swap instructions, which enable atomic operations and help solve the critical-section...
在Windows Communication Foundation (WCF) 中,同步上下文(Synchronization Context)是一个关键概念,它涉及到服务操作的并发执行和线程模型。本篇文章将深入探讨这个主题,并结合实际示例来阐述其工作原理和重要...
ÐArcher Detecting On-Chain-Off-Chain Synchronization Bugs in Decentralized Applications.pdf
部分同步是指在网络节点之间交互作用形成网络结构时,网络中的某些部分能够达到同步状态,而其余部分则未能同步。它是在研究网络动态时经常遇到的现象,特别是在处理复杂网络时。复杂网络广泛存在于自然界和社会系统...
Coexistence of synchronization and anti-synchronization in the unified chaotic systems
**知识点:精准时钟同步——IEEE 1588标准** **一、引言与背景** 在自动化技术领域,精确的时间信息对于分布式系统至关重要。IEEE 1588标准所描述的精密时间协议(Precision Time Protocol,简称PTP)首次实现了...
The 80x86 MOV Instruction 4.8 - Some Final Comments on the MOV Instructions <br>4.9 Laboratory Exercises 4.9.1 The UCR Standard Library for 80x86 Assembly Language Programmers 4.9.2 ...
incorporates reactive power support in the case of voltage sags based on the grid codes’ (GCs) requirements to ride-through the faults and support the grid voltages. A case study of a 1-MW system ...
simulation for channel estimation techniequs using LS, LMMMSE, and computationally efficient LMMMSE methods.
Changing pointers in the change address dialog won't set/override global memrec and address anymore (local now) Fixed show as signed not working for custom types Fixed several issues with the ...