preface

Accidentally pigeon did not update for a few days, this week home. You have to work hard at school!

Only a bald head can be strong

To recap:

  • Multiple threads can get through a door in three minutes!
  • Thread source code analysis

The knowledge of this article mainly refers to the first four chapters of the book “Java Concurrent Programming in Action”, the first four chapters of the book are to explain the fundamentals of concurrency. If we can understand these foundations well, then we can get twice the result with half the effort.

Of course, Java Concurrent Programming in Action is a classic book. I didn’t fully understand it, and it was just a piece of cake. To get a fuller understanding of what I’m talking about, read this book, which is generally good.

To begin with, let’s preview the contents of the first four chapters of Java Concurrent Programming:

Chapter 1 Introduction

  • 1.1 Brief history of Concurrency
  • 1.2 Advantages of threads
  • 1.2.1 Give full play to the powerful capabilities of multi-processors
  • 1.2.2 Simplicity of modeling
  • 1.2.3 Simplified processing of asynchronous events
  • 1.2.4 More responsive user interface
  • 1.3 Risks caused by threads
  • 1.3.1 Security Issues
  • 1.3.2 Activity problem
  • 1.3.3 Performance Problems
  • 1.4 Threads are everywhere

Ps: I will not talk about this part, mainly to lead us to the next knowledge point, interested students can read the original book ~

Chapter 2 thread safety

  • 2.1 What is Thread safety
  • 2.2 atomic
  • 2.2.1 Race conditions
  • 2.2.2 Example: Race conditions in lazy initialization
  • 2.2.3 Compound Operation
  • 2.3 Locking mechanism
  • 2.3.1 built-in lock
  • 2.3.2 reentrant
  • 2.4 Protect status with locks
  • 2.5 Activity and performance

Chapter 3 sharing objects

  • 3.1 the visibility
  • 3.1.1 Failure data
  • 3.1.2 Non-atomic 64-bit operations
  • 3.1.3 Locking and visibility
  • 3.1.4 Volatile variables
  • 3.2 Release and Escape
  • 3.3 Thread Closure
  • 3.3.1 Ad-hoc thread closure
  • 3.3.2 rainfall distribution on 10-12 stack closed
  • 3.3.3 ref;
  • 3.4 invariance
  • 3.4.1 track Final domain
  • 3.4.2 Example: Using Volatile to publish immutable objects
  • 3.5 Security Release
  • 3.5.1 Incorrect publishing: The correct object is corrupted
  • 3.5.2 Immutable objects and initialization security
  • 3.5.3 Common Modes of security Publishing
  • 3.5.4 Immutable objects
  • 3.5.5 Mutable Objects
  • 3.5.6 Sharing Objects Securely

Chapter 4 composition of objects

  • 4.1 Designing thread-safe classes
  • 4.1.1 Collecting Synchronization Requirements
  • 4.1.2 State-dependent Operations
  • 4.1.3 Ownership of the state
  • 4.2 Instance Closure
  • 4.2.1 Java Monitor mode
  • 4.2.2 Example: Vehicle tracking
  • 4.3 Thread-safe delegate
  • 4.3.1 Example: delegation-based vehicle tracker
  • 4.3.2 Independent state variables
  • 4.3.3 When the delegate fails
  • 4.3.4 Publishing underlying state variables
  • 4.3.5 Example: Vehicle tracker with published status
  • 4.4 Add functionality to existing thread-safety classes
  • 4.4.1 Client Locking Mechanism
  • 4.4.2 combination
  • 4.5 Document synchronization policies

So let’s get started

First, the use of multithreading encountered problems

1.1 Thread safety Issues

In the previous article, we talked about threads (multiple threads can get through a door in three minutes!). The main purpose of multithreading is to increase the usage of our application. But at the same time, this will cause us a lot of security problems!

There is no problem if we execute code sequentially (serial -> exclusive) in a single thread. But in a multi-threaded environment (parallelism), if it is not well designed and controlled, it can lead to many unexpected situations, namely thread safety issues

Because threads execute alternately in a multithreaded environment, they typically use multiple threads to execute the same code. If there are shared variables, or some combination of operations, within the same code, we can easily have problems getting the correct result

Here’s a quick example:

  • The following program runs in a single thread without problem.

public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount(a) {
        return count;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...}}Copy the code

But when running in a multithreaded environment, it doesn’t count correctly!

First, it shares the count variable, and second, ++count; This is a combined operation (note that it is not atomic)

  • ++countThe actual operation looks like this:
    • Read the count value
    • The value + 1
    • Write the result to count

Thus, when multithreading is executed, it is likely that the following situation will occur:

  • When thread A reads count as 8, thread B goes into this method and reads count as 8
  • They both add 1 to the value
  • Write the result to count. However, the result written to count is 9
  • That is: two threads come in, but the correct result is that 10 should return, and it returns 9, which is not normal!

If a class consistently behaves correctly when accessed by multiple threads, it is thread-safe!

Rule of thumb: If you can use the THread-safety mechanism provided by the JDK, use it.

Of course, this part is actually the most important part of our study of multithreading, and I will not go into details here. Here is just an overview, these knowledge points in the later study will encounter ~~~

1.3 Performance Problems

The purpose of using multithreading is to increase application usage, but if multithreaded code is not well designed, it does not necessarily improve efficiency. Instead, it reduces efficiency and even causes deadlocks!

In the case of our Servlet, where a Servlet object can handle multiple requests, servlets are naturally multithreaded.

Consider the following example:


public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount(a) {
        return count;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...}}Copy the code

From the above we have stated that the above class is thread-unsafe. The simplest way: If we add synchronized, the built-in lock provided by the JDK, to the service method, we can achieve thread-safety.


public class UnsafeCountingServlet extends GenericServlet implements Servlet {
    private long count = 0;

    public long getCount(a) {
        return count;
    }

    public void synchronized service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {

        ++count;
        // To something else...}}Copy the code

Although thread-safe is implemented, this can cause serious performance problems:

  • Each request must wait for the service method of the previous request to complete the corresponding operation

This leads to: we complete a small function, using multi-threading in order to improve efficiency, but now not properly grasp, but bring serious performance problems!

When using multiple threads: Deadlocks (programs that get stuck) are more serious.

These are the next steps: learn which synchronization mechanism to use to achieve thread-safety, and how performance improves rather than decreases

Release and escape of objects

The book defines release and escape as follows:

Publish enables objects to be used in code outside the current scope

Escape When an object is published that should not be

There are several common ways to escape:

  • Static field escapes
  • Public decorates the GET method
  • Method parameter passing
  • Implicit in this

Static field escape:

Public decorates the get method:

I won’t demonstrate method argument passing because passing an object to another method is already an escape

Here’s an example of this escaping from the book:

Escape is publishing objects where they should not be published. Causing our data to leak out, which creates a security risk! Isn’t that easy to understand?

2.1 Publishing Objects securely

Since there are several escape situations covered above, let’s talk about how to safely publish objects.

There are several common ways to safely publish objects:

  • Class directly in the static domainpublic static Person = new Person();
    • Static initialization is performed by the JVM during class initialization, and there is a synchronization mechanism within the JVM that makes it safe to publish objects this way
  • The corresponding reference is saved toVolatile or AtomicReferance references
    • The visibility and atomicity of references to the object are guaranteed
  • By the final modification
    • The object is immutable, so the thread must be safe to publish
  • Protected by a lock
    • Locks are used and published to ensure that the object does not escape

Three, solve the problems encountered by multithreading

As we can see from the above, using multiple threads can make our system quite complicated. Is the need for us to deal with a lot of things, in order to prevent multi-threading to bring us security and performance problems ~

Here is a brief summary of what we need to know to solve the problem of multi-threading.

3.1 Briefly describe the solution to thread safety

Using multithreading, we must ensure that our threads are safe, this is the most important place!

In Java, there are several ways to implement thread-safety issues:

  • Stateless (no shared variables)
  • Use final to make the reference variable immutable (if the object references other objects, then it needs to be locked either for publication or use)
  • Lock (built-in Lock, display Lock Lock)
  • Use the classes provided by the JDK to implement thread-safety (there are many in this section)
    • Atomicity (like the one abovecount++Operation, you can use AtomicLong to implement atomicity, so you don’t go wrong when adding!
    • Containers (ConcurrentHashMap, etc…)
    • .
  • . , etc.

3.2 Atomicity and visibility

What is atomicity? What is visibility? ConcurrentHashMap is based on the JDK1.8 source code. Those of you who don’t know can go inside.

3.2.1 atomic

A lot of times in multithreading it’s because some operation is not atomic, and it messes up the data. If the data being operated on is atomic, thread-safety issues can be largely avoided!

  • count++, first read, then increment, then assign.If the operation is atomic, then it can be said to be thread-safe.~

Atomic means that an operation is indivisible, such as count++, which is not an atomic operation, but a three-step operation. The JDK provides atomic packages for implementing atomic operations

Others have made tables to classify them. Let’s take a look:

Use these class-related actions to check out his blog as well:

  • Blog.csdn.net/eson_15/art…

3.2.2 visibility

For visibility, Java provides us with a keyword: volatile ~

  • We can simply say that volatile is a lightweight synchronization mechanism

The classic summary of volatile: Volatile is used only to ensure that the variable is visible to all threads, but not atomicity

Let’s break it down to explain:

  • ensureVisibility of this variable to all threads
    • In a multithreaded environment: When this variable is modified, all threads know that the variable has been modified. This is called “visibility”.
  • Atomicity is not guaranteed
    • Modifying a variable (assignment) essentially takes several steps in the JVM, and within those steps (from loading variables to modifying them), it is not safe.

Volatile variables guarantee three things:

  • Once you’re done writing, any thread that accesses the field will get the latest value
  • Before you write, everything that happened before is guaranteed to have happened, and any updated data values are visible, because the memory barrier flusher all previous writes to the cache.
  • Volatile prevents reordering. (Reordering means that the CPU or the compiler may change the order in which a program is executed so that it does not run from the top down. With some unexpected effects). If volatile is declared, the CPU and compiler know that the variable is shared and will not be cached in registers or other invisible places.

In general, volatile is used mostly on flag bits (judgment operations). Volatile should be used to modify variables if:

  • Variables are modified independently of their current value (because volatile does not guarantee atomicity)
  • The variable will not be included in the invariance condition (the variable is mutable)
  • Variables are accessed without locking (locking eliminates the need for lightweight synchronization such as volatile)

References:

  • www.cnblogs.com/Mainz/p/355…
  • www.cnblogs.com/Mainz/p/354…
  • www.dataguru.cn/java-865024…

3.3 Thread Closure

In a multithreaded environment, as long as we don’t use member variables (and don’t share data), there are no thread-safety issues.

We are familiar with the Servlet to take an example, write so many servlets, you have seen us say to lock? All of our data is operated on methods (stack closed), and each thread has its own variables, without interfering with each other!

Operating on methods, our thread is safe as long as we ensure that we do not publish objects on the stack (method) (each variable is only scoped to the current method)

There’s another approach to thread closure that I’ve written about before: ThreadLocal is that simple

Using the API of this class ensures that each thread has its own exclusive variable. Read the above article for details

3.4 invariance

Immutable objects must be thread safe.

The variables we shared above are mutable, which is why thread-safety is a problem. If the state is immutable, then access by any number of threads is fine!

Java provides final modifiers for use. We’ve probably seen more of them, but it’s worth noting:

  • Final simply cannot change the reference to the variable, but the data inside the reference can be changed!

For example, the following HashMap is final. But it only ensures that the object referenced to the hashMap variable is immutable, while the data inside the hashMap is mutable, that is, it can add, remove, and so on to the collection

  • Therefore, it can only be stated that a hashMap is an immutable object reference

  final HashMap<Person> hashMap = new HashMap<>();

Copy the code

Immutable object references still need to be locked when used

  • Or make Person a thread-safe class
  • Because the internal state is mutable, unlocked or Person is not a thread-safe class, operations are dangerous!

To design an object to be immutable, three conditions must be met:

  • The state of an object cannot be changed after it is created
  • All fields of the object are final
  • Object created correctly (no this reference escapes)

We learned that String is an immutable object, but it doesn’t follow the second point (all fields of an object are final) because the JVM is optimized internally. But if we want to design immutable objects ourselves, we need to meet three conditions.

3.5 Thread-safety delegate

A lot of times we don’t necessarily need to lock ourselves to achieve thread safety.

We can use the objects provided by the JDK to achieve thread-safe design:

There are a lot of “tool classes” for us to use, which will be covered in the future study ~~ not here

Four, the last

The correct use of multithreading can improve the efficiency of our application, but also bring us a lot of problems, which we need to pay attention to before using multithreading.

Immutability, visibility, atomicity, thread closure and delegation are all ways to achieve thread safety. By using these tools properly, our programs will be more robust!

You can see that it says locks in many places. But I didn’t introduce it, because I plan to write about it in the next post, so stay tuned

The first four chapters of the book spend 65 pages to explain, but I only use one article to summarize, which is far from enough, students who want to continue to read books ~

Before learning the operating system according to the “Computer operating system – Tang Xiaodan” this book also made a little note, are relatively simple knowledge points. It might be helpful

  • Operating system 1 [Introduction]
  • Operating System Part 2 [Process Management]
  • Operating System Part 3 threads
  • Operating system chapter 4 [Processor scheduling]
  • Operating System Chapter 5 [Deadlock]
  • Operating System Chapter 6 [Memory Management]
  • Operating System Part 7 Device Management

References:

  • Java Core Technology Volume 1
  • Java Concurrent Programming
  • Computer Operating System – Dennis Tang

If the article has the wrong place welcome to correct, everybody exchanges with each other. Students who are used to reading technical articles on wechat and want to get more Java resources can follow the wechat public account :Java3y. Thanks for your support! I hope I can introduce to other friends in need

Open source project (6 K STAR) :Github.com/ZhongFuChen…

If you want to follow my updated articles and shared dry goods in real time, you can search Java3y on wechat.

The content of the PDF document is typed by hand. If you don’t understand anything, you can ask me directly (the official account has my contact information).