This article is reprinted from the WeChat public account "Huai Meng Zhuima", which can be followed through the following QR code. To reprint this article, please contact the Huai Meng Zhuima public account. 1. Synchronize access to shared data question Concurrent programs are more complex to design than single-threaded programs, and failures are harder to reproduce. However, multithreading is unavoidable, because it is an effective way to get the best performance from multi-core computers. When it comes to concurrency, if mutable data is involved, this is where we need to focus our thinking. When facing concurrent access to mutable data, what methods can ensure thread safety? Answer
1. Keyword synchronized: synchronized is a powerful tool to ensure thread safety. It can ensure that only one thread can execute a method and modify a variable data at the same time. However, it is not entirely correct to simply understand it as mutually exclusive. It has two main meanings: In addition, the Java language specification guarantees that reading and writing a variable is atomic, unless the variable is a double or long, even without synchronization. Considering such an example, the thread achieves the function of gracefully stopping the thread by polling the flag bit. The sample code is as follows:
The mutable data, that is, the state variable stopRequested, is modified by the synchronization method, which ensures that after stopRequested is modified, it can be immediately visible to other threads. 2. Keyword volatile: The most important function of volatile is to ensure data visibility. When one thread modifies variable data, another thread will immediately know the latest data. In the above example, because the read and write of the stopRequested variable are atomic, the use of synchronized only takes advantage of its data visibility. However, since synchronized will lock, if you want better performance, the above example can be modified using volatile:
But it should be noted that volatile does not guarantee atomicity, such as the following example:
Although volatile is used, the ++ operator is not atomic, so it will fail when used with multiple threads. The ++ operator performs two operations: 1. Read the value; 2. Write back the new value (equivalent to the original value + 1). If the second thread reads this field while the first thread is reading the old value and writing the new value, an error will occur and they will get the same SerialNumber. At this time, synchronized is needed to make mutual exclusive access between threads to ensure atomicity. Summarize The best way to solve this problem is to avoid sharing mutable data between threads as much as possible and limit mutable data to a single thread. If you want multiple threads to share mutable data, both reading and writing need to be synchronized. 2. Use the method of creating threads with caution question Because concurrent programs are prone to thread safety issues and thread management is also very complicated, when creating a thread, do not create it manually through Thread. Instead, use the Executor framework to manage it. What are the advantages of Executor? Answer
in conclusion When it comes to multi-threaded programs, do not use Thread to create threads. Instead, use executor to manage and create threads. Its biggest advantage is the decoupling between work units (threads) and tasks. 3. Prioritize concurrent tools question It is difficult to ensure thread safety in high-concurrency programs, and once a problem occurs, it is also difficult to troubleshoot and analyze the cause. The juc package provides many thread-safe tools. In actual development, we should use more of these tools whose performance has been verified, which makes our development very convenient and ensures the stability of our code. What are the commonly used concurrency tools? Answer The concurrent tools under the juc package are divided into three categories: 1. The executor framework responsible for managing threads; 2. Concurrent collections; 3. Synchronizers. Among them, the executor responsible for managing threads has been mentioned in Article 68 and will not be described separately.
in conclusion The juc package provides us with a variety of thread-safe data structures. In actual development, we should use these tools with guaranteed performance and safety, rather than reinventing the wheel, which is difficult to ensure safety. For example, in the previous code, "producer-consumer" is implemented using wait and notify, which makes the code difficult to maintain. If the BlockingQueue with blocking operations is used, the code will be more concise and the logic will be clearer. 4. Thread safety documentation question There are several wrong statements: These are two common misconceptions. In fact, there are multiple levels of thread safety. So, how should thread safety documentation be established?
Answer
At this time, the private lock object can only be accessed from within the current class, and cannot be accessed from outside, so it is impossible to interfere with the synchronization of the current class, and "denial of service attacks" can be avoided. However, this method is only suitable for the "unconditional thread safety" level, and cannot be applied to the "conditional thread safety" level. The conditional thread safety level must specify in the document which lock should be obtained when calling a method. Summarize Each class should clearly document its thread-safe properties using rigorous instructions or thread-safe annotations. Conditionally thread-safe classes should state which methods require synchronized access and which locks are acquired. Unconditionally thread-safe classes can use private lock objects to prevent "denial of service attacks." When it comes to thread-safe issues, documentation should be written strictly in accordance with the specifications. 5. Use delayed initialization with caution
Lazy initialization is the act of delaying the initialization of a field until its value is needed. If the value is never needed, the field will never be initialized. This approach applies to both static and instance fields. Like most optimizations, premature optimization is the source of most errors. So what are some reliable ways to achieve thread-safe lazy initialization?
Here is how you would normally initialize instance fields, but note the use of the final modifier:
Now to lazily initialize this instance field, there are several ways: 1. Synchronization method: When instantiating domain values, you can use synchronization methods to ensure thread safety, such as:
2. Static inner class: In order to reduce the synchronization access cost of the above method, you can use the static inner class method, which is called the lazy initialization holder class mode. Under the optimization of the JVM, this method can not only achieve the effect of lazy initialization, but also ensure thread safety. The sample code is:
3. Double check: This mode avoids the locking overhead when accessing the domain again after initialization (in ordinary methods, synchronized methods are used to synchronize methods, and locks are required every time the method is accessed). The idea of this mode is: check the value of the domain twice, unlock it during the first check to see if it is initialized; lock it during the second check. Only when the second check indicates that it has not been initialized, the computeFieldValue method will be called to initialize it. If it has been initialized, it will not be locked. In addition, it is very important that the domain is declared as volatile. The sample code is:
in conclusion Most normal initializations are better than lazy initialization. If you must perform lazy initialization, use double detection for instance fields. For static fields, you can use the feature that static inner classes are initialized only when they are first accessed, and use static inner classes to complete lazy initialization. 6. Don’t rely on the thread scheduler
When there are multiple threads running, the thread scheduler decides which threads will run and allocates CPU time slices. However, the scheduling strategies adopted by most systems are different. Therefore, any concurrent program that relies on the thread scheduler to achieve program performance and correctness is unsafe and non-portable. So, what are some good ways to write portable and robust concurrent programs?
Never let your program rely on the thread scheduler, as this will lose robustness and portability. Features such as Thread.yield and thread priority are the least portable and should not be used in your program. 7. Avoid using thread groups
In addition to threads, locks, and monitors, the thread system also provides another abstract unit: thread groups. The original intention of the thread group design was to isolate applets and achieve security. However, in reality, the expected security is not achieved, and it is so poor that it is not even mentioned in the JAVA security model. In addition to the poor security, what other defects are there?
Besides the security not being up to expectations, there are few basic features available; The ThreadGroup API is very fragile;
Thread groups don't provide a lot of useful functionality, and many of the features they do provide are flawed. When managing threads or dealing with thread group logic, you should consider using executors. |
<<: The US court's suspension of the TikTok ban will take effect this Sunday
>>: Three-minute review! A quick overview of 5G industry development trends in September
The increasing deployment of 5G has brought about...
As an important driving force for the digital tra...
As new technologies continue to emerge, more and ...
Reader Question: Although I am also in the IT ind...
Every spring is a good time for major mobile phon...
Nowadays, few people send text messages except fo...
A few days ago, Xiao Wei shared with everyone the...
[[375297]] Recently, Wu Hequan, an academician of...
For data center operators, the idea of a wirele...
Introduction to FTP FTP (File Transfer Protocol) ...
[[393969]] The data center industry is experienci...
OTN (Optical Transport Network) is a hierarchical...
LiteServer is carrying out a promotion for its 15...
Mobile networks have entered the 5G era, and thei...
LOCVPS (Global Cloud) officially launched the Dou...