Welcome to ReentrantReadWriteLock. Today we are going to talk about the design of read and write state.
I believe that many readers, when looking at the JDK source code, will see the bit operation code, perhaps some people and a xing is a career change, the lack of computer related basic knowledge, see is confused.
As a result, some people are simply discouraged, while others choose to take the word for it and skip the details.
But a seed of doubt was planted in our minds: “How can we achieve this with bitwise operations?” .
ReentrantReadWriteLock is designed to use bits to start today’s topic.
A bit operation code
We came to ReentrantReadWriteLock Sync inner class, found this code (back to RRW)
// The offset bit
static final int SHARED_SHIFT = 16;
// Basic unit of read lock count
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
// Maximum number of read and write locks that can be reentrant
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
// Get the condition of the lower 16 bits
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// Get the read lock reentrant number
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// Get the write lock reentrant number
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
Copy the code
What is all this bit code for?
Because the state int in RRW maintains both read and write lock states, RRW is implemented by high and low cutting.
Int is 4 bytes, 8 bits per byte, 32 bits in total, cut it up, 16 bits higher for read, 16 bits lower for write.
The advantage of doing this is to save resources, just as the boss treats you like two people in real life.
At this point, we also understand that the above bit operation code is to complete the high and low bit cutting.
Read lock bit operation
// The offset bit
static final int SHARED_SHIFT = 16;
// Basic unit of read lock count
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
Copy the code
The read lock uses 16 bits higher than the read lock. Each time a read lock is obtained, the basic unit of the read lock count is the 16 bits higher than 1, that is, 1 moves 16 bits to the left (1 << 16).
1 left shift 16 bits is 65536, every time to get a read lock success +65536, then some readers jump to ask, is not +1, how to become +65536, this is wrong ah.
Take a look at this code
// The offset bit
static final int SHARED_SHIFT = 16;
// Get the read lock reentrant number
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
Copy the code
SharedCount (sharedCount, sharedCount, sharedCount, sharedCount, sharedCount);
A xing takes 16 steps forward and 16 steps backward, and then returns to the origin. If 1 moves 16 bits to the left, it equals 65536, and if 65536 moves 16 bits to the right, it equals 1.
For example, if we get 3 read locks, 65536 * 3 = 196608, the formula is that 3 moves 16 bits to the left equals 196608, 196608 moves 16 bits to the right equals 3.
Although we get +65536 every time we get a read lock, we do a 16-bit right shift when we get a read lock, so the effect is the same as +1.
Write lock operation
// The offset bit
static final int SHARED_SHIFT = 16;
// Get the condition of the lower 16 bits
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// Get the write lock reentrant number
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
Copy the code
The rest of the write lock is very simple, do not move left and right to obtain the lower 16 bits, as long as the high 16 bits of all zeros.
It is the same as the normal number, but because the 16 bits are added with 0, the value ranges from 0 to 65535, that is to say, write lock success directly +1 is good.
Let’s look at the ‘EXCLUSIVE_MASK’ variable. Shift 1 16 bits right to -1 to get 65535,65535 binary 11111111111111111.
Now let’s look at the ‘exclusiveCount’ function, which does the bit operation &, which is also called ‘and’.
The “and” operations are two binary, one-digit operations, and the rules are as follows
0, 0 = 0
0 and 1 = 0
1 & 0 = 0
1 and 1 = 1
If both corresponding bits are 1, the result is 1; otherwise, it is 0
For those of you who may be confused, here’s a picture of a 16-bit binary and operation
We found that if one of the “and” operations is 0, the result must be 0, so to cut the lower 16 bits, we can use ampersand to do this.
As can be seen from the above figure, the upper 16 bits of ‘EXCLUSIVE_MASK’ are all 0, the lower 16 bits are all 1, and its & variable, the upper 16 bits will all become 0, the lower 16 bits will remain, and finally achieve the effect of obtaining the lower 16 bits.
C & EXCLUSIVE_MASK, assuming c is 1, &
This may not feel too much, let’s increase the value a little bit, assuming that C is 65536 and 65537, the process of & is shown below
Now you get a sense of it. The top 16 bits of C will all go to 0, and the bottom 16 bits will stay the same, eventually achieving the effect of getting the bottom 16 bits.
The value of ‘EXCLUSIVE_MASK’ ranges from 0 to 65535. Therefore, the value of ‘C’ cannot exceed 0 to 65535. If the value exceeds 0, you can use & ‘EXCLUSIVE_MASK’ to return to 0 to 65535.
Ask a question
“Star” : how to serialize and deserialize int?
“Dude, I’m just going to use Integer. Number implements Serializable interface.”
“A xing” : Instead of using Serializable, how about writing one yourself?
Meng Xin: Ah, it’s…
In order to let you better digest the previous content, a star hand in hand with you to achieve int and byte mutual transfer.
Int takes 4 bytes, 8 bits per byte, 32 bits in total.
Int to byte array
The idea is very simple, we just need to cut from right to left 8 bits one by one, and then store into the byte array, the code is as follows:
public static byte[] intToBytes(int n) {
// An array of 4 bytes
byte[] buf = new byte[4];
for (int i = 0; i < buf.length; i++) {
// Loop over 8 bits to the right to store it in the array
buf[i] = (byte) (n >> (8 * i));
}
return buf;
}
Copy the code
The process diagram is as follows
Byte Converts an array to an int
We converted from int to byte[], and now we want to convert from byte[] to int as follows
public static int bytesToInt(byte[] buf) {
return buf[0] & 0xff
| ((buf[1] < <8) & 0xff00)
| ((buf[2] < <16) & 0xff0000)
| ((buf[3] < <24) & 0xff000000);
}
Copy the code
The code involves the “left shift, and, or” bit operation, left shift and and we said before, there is also an or, or and the same, but the rules are different, and the rules for or are as follows
0, 0 = 0
0 and 1 = 1
1 & 0 = 1
1 and 1 = 1
If both corresponding bits are 0, the result is 0; otherwise, it is 1
0 XFF binary is 11111111 for each additional 2 0, 0 XFF behind, the effect is equal to the left eight and so on, so we finally is to use the < <, |, &, 0 XFF to restore.
The process diagram is as follows
summary
In ReentrantReadWriteLock, read and write states share one state. It cleverly uses high and low bits to save resources. In the entire implementation process, bit operations are used to cut high and low bits.
Good historical articles recommended
- Easy to understand ReentrantLock, don’t understand you to chop me
- Word long | 16 picture AbstractQueuedSynchronizer
- LockSupport for small white look
- 13 pictures, in-depth understanding of Synchronized
- From shallow to deep CAS, Xiao Bai can also align with BAT interviewers
- Small white also can understand the Java memory model
- Nanny Level teaching, 22 images uncover ThreadLocal
- Can process, thread and coroutine be confused? A penny for you to know!
- What is thread safety? This article will give you insight
About me
Here is A star, a Java program ape who loves technology. The public account “program ape A Star” will regularly share the operating system, computer network, Java, distributed, database and other high-quality original articles. 2021, grow together with you on the road of Be Better! .
Thank you very much for your little brothers and sisters to see here, the original is not easy, the article can be helpful to pay attention to, point a like, share and comment, are support (don’t want white whoring)!
May you and I both go where we want to go. See you in the next article