Java concurrent reading notes: thread safety and mutually exclusive synchronization

This article refers to many famous books to form reading notes, which is convenient for deepening memory.

Previous portal: Java concurrent reading notes: JMM and reordering

Causes of thread insecurity

When a variable is read by multiple threads and written by at least one thread, if the read-write operation does not follow the happens before rule, there will be a hidden danger of data competition. If the correct synchronization method is not given, the thread will be unsafe.

What is thread safety

Brian Goetz defined it in the practice of Java Concurrent Programming as follows:

Zhou Zhiming mentioned in the in-depth understanding of Java virtual machine that when there is shared data among multiple threads, these data can be classified according to the thread safety level:

Immutable

Immutable objects must be thread safe. As long as an immutable object is correctly constructed, its state in multiple threads is consistent. For example, modify the object with the final keyword:

Types that meet immutability requirements in Java API: String class, enumeration class, numeric wrapper type (such as double) and big data type (BigDecimal).

Absolute thread safety

That is, it fully meets the above definition of thread safety.

In fact, meeting this definition requires a lot of costs. In fact, most of the classes marked thread safe in Java are not thread safe (such as vector), because it still needs to take synchronization measures at the caller. Absolute thread safe classes in Java: copyonwritearraylist and copyonwritearrayset.

Relative thread safety

That is what we usually call thread safety. Most thread safety classes in Java belong to this category, such as vector, hashtable, collections, collections wrapped by the synchronizedcollection () method of the tool class, and so on. Take vector as an example: if a thread is traversing a vector and a thread is adding the vector at the same time, a concurrentmodificationexception, that is, the fail fast mechanism, will appear 99% of the time.

Thread compatible

The object itself is not thread safe. You can ensure the safe use of the object in the concurrent environment by correctly synchronizing the call segment. As we learned earlier, ArrayList and HashMap corresponding to vector and hashtable respectively.

Objects are modified by synchronized keyword to achieve synchronization effect. It is safe in itself, but the efficiency will be much lower.

Thread opposition

No matter whether the caller takes synchronization measures or not, it cannot execute correctly in a multithreaded environment. Typical thread opposition in Java: suspend () and resume () methods in thread class: if two threads manipulate a thread object at the same time, one attempts to suspend and the other attempts to recover, there will be a deadlock risk and it has been discarded.

Common opposition: system setIn(),System. Setout() and system runFinalizersOnExit()。

Mutually exclusive synchronization for thread safety

Mutually exclusive synchronization is also called blocking synchronization (because mutually exclusive synchronization will cause performance problems due to thread blocking and wake-up). It is one of the methods to achieve thread safety. The other is non blocking synchronization, which can be learned later.

Mutually exclusive synchronization: ensure that the shared data is used by only one thread at the same time under concurrency.

Synchronized built-in lock

Using synchronized keyword to modify methods or code blocks is the most basic means of mutually exclusive synchronization.

Synchronized is a built-in locking mechanism that enforces atomicity provided by Java. In terms of the definition of synchronized code blocks:

synchronized(lock){
    //访问或修改被锁保护的共享状态
}

It consists of two parts: 1. Reference of lock object 2. Code block protected by lock.

Each Java object can be used as a lock object for synchronization. We call this kind of lock monitor locks, also known as built-in locks.

It can be understood as follows: the thread needs to obtain the lock object before entering synchronized. The lock will be released when the thread ends normally or throws an exception.

The lock object completes mutual exclusion well. Suppose a holds the lock. At this time, if B also wants to access the lock, B will fall into blocking. Only after a releases the lock can b stop blocking.

Lock is object

//普通同步方法
public synchronized void do(){}
//静态同步方法
public static synchronized void f(){}
//锁对象为TestLock的类对象
synchronized (TestLock.class){    
    f();
}

Clear: the synchronized method is not different from the code block in nature. The method is only a short description of the code block across the whole method body, and the lock is the object of the method itself (a static modified method, and the object is the current class object). For this part, please refer to the synchronized deep parsing of Java concurrency

Do you want to release the lock

Release the lock:

The lock is not released:

Implementation principle

By right Class file decompilation shows that the synchronization method passes acc_ Synchronized modification, code block synchronization is realized by using two instructions: monitorenter and monitorexit.

Although the implementation details of the two are different, they are essentially synchronized by the JVM based on entering and exiting the monitor object. The requirements of the JVM are as follows:

What is reentry?

Reentry means that any thread can acquire the lock again after acquiring the lock without being blocked by the lock. Synchronized implicitly supports reentry, so it will not lock itself.

This reflects the function of the lock counter: one lock is obtained plus one, and one lock is released minus one. No matter how many times it is obtained or released, as long as the count is zero, it means that the lock is successfully released.

Reentrantlock

Reentrantlock is located in Java util. Under concurrent (j.u.c) package, it is the implementation class of lock interface. The basic usage is similar to synchronized. Both have reentrant and mutually exclusive features, but have extended functions.

Renntrantlock official recommended basic writing:

class X {
    //定义锁对象
    private final reentrantlock lock = new reentrantlock();
    // ...
    //定义需要保证线程安全的方法
    public void m() {
        //加锁
        lock.lock();  
        try{
        // 保证线程安全的代码
        }
        // 使用finally块保证释放锁
        finally {
            lock.unlock()
        }
    }
}

Mutex at API level

Reentrantlock is expressed as a mutex at the API level, which is completed through the lock () and unlock () methods. It is explicit, while synchronized is expressed as a mutex at the native syntax level, which is implicit.

Wait for interruptible

When the holding thread does not release the lock for a long time, the waiting thread can choose to give up waiting or deal with other things.

Fair lock

Reentrantlock lock is a fair lock, which ensures that multiple waiting threads obtain locks in turn according to the time sequence of applying for locks, while synchronized is an unfair lock.

Lock binding

A reentrantlock object can bind multiple condition objects at the same time.

JDK1. Before 6, reentrantlock was ahead of synchronized locks in performance, but jdk1 Version 6 implements various lock optimization technologies, and subsequent performance improvements will be more inclined to the native synchronized.

Reference data: Practice of Java Concurrent Programming, art of Java Concurrent Programming, in-depth understanding of Java virtual machine

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>