How to ensure thread safety? Synchronized, ReentrantLock, Atomic usage scenarios

Thread safety solution
synchronized, ReentrantLock, Atomic usage scenario description

In the actual development process, if the service volume and requests are frequent, concurrency will often be encountered. At this time, if there is no processing, a lot of illegal data will appear. At this time, it is necessary to solve the problem of thread safety, and at this time, the lock mechanism in java can be used. Commonly used are java key synchronized, reentrant lock ReentrantLock, and Atomic
or Concurrent security types under concurrent packages.

Synchronized usage scenarios:

In the case where resource competition is not very intense, and occasional concurrency occurs and synchronization is required, synchronized is very suitable. The reason is that the compiler usually optimizes synchronized as much as possible, and the readability is very good, which can be understood by programmers who have not used the 5.0 multi-threading package. Multiple methods can be locked (synchronized method), and objects can also be locked (synchronized code is fast).

/**
 * synchronized用id
 */
private static volatile  Long  syncId=0L;

/**
 * synchronized方式获取id 同步方法
 * @return
 */
public static synchronized  Long getSyncId1(){
    
    
    syncId++;
    return syncId;
}

/**
 * synchronized方式获取id 同步代码块
 * @return
 */
public static Long getSyncId2(){
    
    
    synchronized (syncId){
    
    
        syncId++;
        return syncId;
    }
}

The code is highly readable. After all, it is a keyword of java, and its execution priority is high. Once the synchronized keyword is placed, the problem of thread safety will be solved. But there is still a problem. When the current resource competition is fierce, some threads cannot acquire locks for a long time. At this time, there will be a lock upgrade process, and the lock upgrade process is irreversible. When going from a lightweight lock to a biased lock, and then to a heavyweight lock. Performance will be greatly reduced. Other methods can be used to lock when resource competition is fierce.

ReentrantLock usage scenarios:

ReentrantLock provides a variety of synchronization, such as time-limited synchronization, synchronization that can be interrupted (synchronized synchronization cannot be interrupted), etc. In the case of less competitive resources, the performance is slightly worse than synchronized. But when the synchronization is very intense, the performance of synchronized can drop dozens of times at once. And ReentrantLock can also guarantee normal performance.
And this lock can be defined as a fair lock or an unfair lock.
/**
* ReentrantLock uses id
*/
private static volatile Long lockId=0L;

/**
 * ReentrantLock公平锁
 */
private static final ReentrantLock reentrantLock = new ReentrantLock(true);


/**
 *  ReentrantLock方式获取id
 * @return
 */
public static Long getLockId(){
    reentrantLock.lock();
    try {
        lockId++;
        return lockId;
    }catch (Exception e){
        e.printStackTrace();
        return getLockId();
    }finally {
        reentrantLock.unlock();
    }
}

I use fair locks as the demonstration object here. ReentrantLock can also check the status of the lock and whether the lock is locked.
You can check how many threads are currently waiting for the lock. But because ReentrantLock is a pessimistic lock, resources will be locked when locking, and the performance will not be as good as CAS optimistic lock when reading frequently. Therefore, optimistic locks are frequently used for reading, and pessimistic locks are frequently used for writing .

Atomic or Concurrent usage scenarios:
Similar to the above, the performance is slightly worse than synchronized when it is not intense, and it can maintain normal when it is intense. When it is intense, the performance of Atomic will be about twice that of ReentrantLock. But it has a disadvantage, that is, only one value can be synchronized, and only one Atomic variable can appear in a piece of code, and more than one synchronization is invalid. Because he cannot synchronize between multiple Atomics.

/**
 * Atomic用id
 */
private static volatile AtomicLong atomicId=new AtomicLong(0L);

/**
 * Atomic方式获取id
 * @return
 */
public static Long getAtomicId(){
    return  atomicId.addAndGet(1);
}

For other types such as Map and Set, you can use thread-safe data types such as ConcurrentHashMap and ConcurrentHashSet under the concurrent package.

/**
 * 线程安全的hashMap
 */
private static ConcurrentHashMap<String,String> hashMap = new ConcurrentHashMap<>();

public static void put(String key,String value){
    hashMap.put(key,value);
}

public static String get(String key{
   return hashMap.get(key);
}

The internal implementation of ConcurrentHashMap is the optimistic lock of CAS. When the lock cannot be obtained, it will start spinning until the next time the lock is obtained.

Guess you like

Origin blog.csdn.net/qq_38420688/article/details/108843314