Java concurrent programming and java concurrency
This article records the immutability of the policy to ensure concurrency security.
(Note: It is Immutable, not Invariant !)
Organizes a series of actions into an atomic operation to ensure immutability conditions, or uses a synchronization mechanism to ensure visibility, to prevent reading of invalid data or changing objects to an inconsistent state, these problems are caused by shared variable data.
If we can ensure that the data is not changeable, we naturally don't have to consider these complicated problems.
Immutable objects must be thread-safe.
It is simple and simple. an unchangeable object has only one State and is controlled by the constructor.
Therefore, it is very easy to judge the state of an unchangeable object.
When we share a mutable object, it is unpredictable to change its state, especially when it is passed as a parameter to a method that can be overwritten, even worse, the client code can retain the reference of this object, which means the time for changing the state is equally unpredictable.
Compared with the sharing of mutable objects, the sharing of immutable objects is much simpler, and there is almost no need to consider getting a snapshot.
So now we have a new question: how can we make the status immutable?
The "immutable" statement is not clearly defined either in JLS or elsewhere, but immutable is definitely not as simple as adding a final modifier, for example, the field modified by final references a mutable object, while final only guarantees that the point of reference will not change.
Yes, immutable objects and immutable object references are two different things.
We have three conditions for building an immutable object (although it is a "condition", but it is not so hard, it can be regarded as a suggestion ):
1. After an object is created, its status is not changeable.
2. All fields of the object are final
3. The reference is not escaped during creation, so the object is created correctly.
Here is an example of the above three items:
123456789101112131415161718192021 |
public final class ThreeStooges { private final Set<String> stooges = new HashSet<String>(); public ThreeStooges() { stooges.add( "Moe" ); stooges.add( "Larry" ); stooges.add( "Curly" ); } public boolean isStooge(String name) { return stooges.contains(name); } public String getStoogeNames() { List<String> stooges = new Vector<String>(); stooges.add( "Moe" ); stooges.add( "Larry" ); stooges.add( "Curly" ); return stooges.toString(); } } |
Let's check whether three conditions are met:
1. After an object is created, make sure its status is changeable. Does it change? We first modified stooges with private, in the two public methods provided, the first method returns boolean, and the second method getStoogeNames re-creates a stooges and ensures the same logic rather than directly referencing stooges field.
2. All fields of an object are final. Obviously, we use final to describe the object to prevent the object state from changing its reference during the object lifecycle.
3. the reference is not escaped during creation. When stooges declares, we specify the reference and initialize it in the constructor. No external methods can be referenced to this state and adapted.
I have to say that this final modifier is the key.
The most intuitive impression of the final keyword is that if the reference point of an object modified with final is not changed (it is difficult to express it clearly in any way, but you know), but even if you reference a variable instance, you can simplify the process by adding final to the status, it's much easier to analyze the total score of basically immutable objects ....
The final and synchronized keywords also have multiple semantics, that is, they can ensure the security of the initialization process, so that they can be freely shared and do not need to be synchronized (This synchronization process does not include visibility ).
The following is an example of using final (more specifically, it should be immutable) to ensure the atomicity of operations (to ensure variability.
After receiving a parameter, a Servlet transmits the parameter to the factor method for calculation and responds to the result.
Assuming that this factor method is very time-consuming, we come up with a method to temporarily alleviate this situation, that is, if the parameters of the next request are the same as those of the previous request, the results in the response cache will be returned.
That is to say, each request requires an additional step, that is, to determine whether the number of the request is the same as that in the cache. If the number is different, re-calculate the request. This segment is not an atomic operation, and issues that may damage the variability conditions.
To solve this problem, we can use synchronized to ensure its atomicity, but here we use another method to use immutable objects:
1234567891011121314151617 |
public class OneValueCache { private final BigInteger lastNumber; private final BigInteger[] lastFactors; public OneValueCache(BigInteger i, BigInteger[] factors) { lastNumber = i; lastFactors = Arrays.copyOf(factors, factors.length); } public BigInteger[] getFactors(BigInteger i) { if (lastNumber == null || !lastNumber.equals(i)) return null ; else return Arrays.copyOf(lastFactors, lastFactors.length); } } |
How is this immutable object designed?
First, we ensure that all States are modified with final and initialized in the unique constructor. Note that we use Arrays to initialize lastFactors. copyOf ensures its correct structure, that is, preventing escape.
Then there is the only public method. This method returns exactly the calculated factors, but we cannot directly return factors. We also use Arrays. copyOf to prevent escape.
The following is the cache Servlet. Only one field of the entire object is the cache. We use volatile to ensure the visibility during concurrency, that is, when thread A changes the reference, thread B can immediately see the new cache.
123456789101112131415161718192021222324 |
public class VolatileCachedFactorizer extends GenericServlet implements Servlet { private volatile OneValueCache cache = new OneValueCache( null , null ); public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = cache.getFactors(i); if (factors == null ) { factors = factor(i); cache = new OneValueCache(i, factors); } encodeIntoResponse(resp, factors); } void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) { } BigInteger extractFromRequest(ServletRequest req) { return new BigInteger( "7" ); } BigInteger[] factor(BigInteger i) { return new BigInteger[]{i}; } }
|
JAVA concurrent programming practices
I don't want to continue with the translation. It is indeed difficult to understand some words, but as far as the content is concerned, this book should be regarded as a pearl in the JAVA concurrency field, the path to concurrency is shown in the light. (If you have the ability to read the English version, since you have to talk about the Chinese version, it is better to talk about it, at that time, I felt that when I was reading this book, I used a new idiom to describe it-it was not clear. In the past two years, concurrency has become popular. In fact, it has been popular for decades, it can be responsibly said that almost all of the Chinese characters you can see on the Internet about JAVA concurrency can be found in this book. I personally think we should introduce JMM, which can at least bring up a lot of interest. The translation of this book is obscure. It is not a level problem if you try to understand it without a hard head, it is hard to imagine that it took 10 hours to complete the book again. In fact, after reading this book, you should be able to understand the concurrent libraries and frameworks in the JAVA field that are currently quite awesome. Of course, you will also be particularly careful with the use of locks, release visibility, activity, performance, and testing. This book covers a wide range of contents, and cannot be completely digested at once. The example is representative and targeted. It is worth reading this book again when you face concurrency, if there are not many contacts or you just know about concurrency, it is also very suitable for you to have a good understanding of the concurrency field in the JAVA World, and you will have a better understanding of the re-reading. JAVA concurrency is really amazing, it cannot be described as powerful and flexible. Of course, this power is costly. I personally think that after reading this, I should read the concurrent library in JDK carefully. Although this book is about JAVA concurrency, if you have other language experience, you should also recommend reading this book, so that you can understand the JAVA World's concurrency is so wonderful and complicated. Although the details in the book cannot be fully remembered, it is enough to create an index. You can read it again later.
JAVA concurrent programming practices
I don't want to continue with the translation. It is indeed difficult to understand some words, but as far as the content is concerned, this book should be regarded as a pearl in the JAVA concurrency field, the path to concurrency is shown in the light. (If you have the ability to read the English version, since you have to talk about the Chinese version, it is better to talk about it, at that time, I felt that when I was reading this book, I used a new idiom to describe it-it was not clear. In the past two years, concurrency has become popular. In fact, it has been popular for decades, it can be responsibly said that almost all of the Chinese characters you can see on the Internet about JAVA concurrency can be found in this book. I personally think we should introduce JMM, which can at least bring up a lot of interest. The translation of this book is obscure. It is not a level problem if you try to understand it without a hard head, it is hard to imagine that it took 10 hours to complete the book again. In fact, after reading this book, you should be able to understand the concurrent libraries and frameworks in the JAVA field that are currently quite awesome. Of course, you will also be particularly careful with the use of locks, release visibility, activity, performance, and testing. This book covers a wide range of contents, and cannot be completely digested at once. The example is representative and targeted. It is worth reading this book again when you face concurrency, if there are not many contacts or you just know about concurrency, it is also very suitable for you to have a good understanding of the concurrency field in the JAVA World, and you will have a better understanding of the re-reading. JAVA concurrency is really amazing, it cannot be described as powerful and flexible. Of course, this power is costly. I personally think that after reading this, I should read the concurrent library in JDK carefully. Although this book is about JAVA concurrency, if you have other language experience, you should also recommend reading this book, so that you can understand the JAVA World's concurrency is so wonderful and complicated. Although the details in the book cannot be fully remembered, it is enough to create an index. You can read it again later.