当前位置:网站首页>Talk about 10 tips to ensure thread safety

Talk about 10 tips to ensure thread safety

2022-06-09 04:47:00 pythonxxoo

High quality resource sharing

Learning route guidance ( Click unlock ) Knowledge orientation Crowd positioning
🧡 Python Actual wechat ordering applet 🧡 Progressive class This course is python flask+ Perfect combination of wechat applet , From the deployment of Tencent to the launch of the project , Create a full stack ordering system .
Python Quantitative trading practice beginner Take you hand in hand to create an easy to expand 、 More secure 、 More efficient quantitative trading system

Preface

For students engaged in back-end development , Thread safety The problem is something we need to consider every day .

Thread safety is a popular topic : Mainly in the multi-threaded environment , Different threads read and write public resources at the same time ( Critical resources ), Data exception caused by .

such as : Variable a=0, Threads 1 Give this variable +1, Threads 2 Also give this variable +1. here , Threads 3 obtain a The value of may not be 2, It is 1. Threads 3 This is not to get the wrong data ?

Thread safety issues can directly lead to data exceptions , This will affect the normal use of business functions , So this problem is still very serious .

that , How to solve the thread safety problem ?

Let's talk with you today , Thread safe 10 A little trick , I hope it helped you .

1. No state

We all know that only multiple threads can access public resource When , Data security issues may arise , So if we don't have public resources , Is there no such problem ?

for example :

public class NoStatusService {

    public void add(String status) {
        System.out.println("add status:" + status);
    }

    public void update(String status) {
        System.out.println("update status:" + status);
    }
}

In this case NoStatusService There is no public resource defined , In other words No state Of .

In this scenario ,NoStatusService Class must be thread safe .

2. immutable

If the common resource accessed by multiple threads is immutable Of , There will be no data security issues .

for example :


public class NoChangeService {
    public static final String DEFAULT\_NAME = "abc";

    public void add(String status) {
        System.out.println(DEFAULT_NAME);
    }
}

DEFAULT_NAME Defined as staticfinal The constant , It will not be modified in a multithreaded environment , So in this case , There will be no thread safety issues .

3. No permission to modify

occasionally , We define public resources , But this resource only exposes the read permission , No permission to expose changes , This is also thread safe .

for example :

public class SafePublishService {
    private String name;

    public String getName() {
        return name;
    }

    public void add(String status) {
        System.out.println("add status:" + status);
    }
}

In this case , No external exposure modification name Entry to the field , So there's no thread safety issue .

3. synchronized

Use JDK Provided internally Synchronization mechanism , This is also a means of using more , It is divided into : Synchronization method and Synchronization code block .

We prefer to use synchronized code blocks , Because the granularity of synchronous method is the whole method , Range is too big , relatively speaking , It consumes more code performance .

Actually , Inside each object there is a lock , Only those who snatched the lock Threads , Is allowed to enter the corresponding code block and execute the corresponding code .

After the contemporary code block is executed ,JVM The bottom layer will automatically release the lock .

for example :

public class SyncService {
    private int age = 1;
    private Object object = new Object();

    // Synchronization method 
    public synchronized void add(int i) {
        age = age + i;        
        System.out.println("age:" + age);
    }

    
    public void update(int i) {
        // Synchronization code block , Object lock 
        synchronized (object) {
            age = age + i;                     
            System.out.println("age:" + age);
        }    
     }
     
     public void update(int i) {
        // Synchronization code block , Kind of lock 
        synchronized (SyncService.class) {
            age = age + i;                     
            System.out.println("age:" + age);
        }    
     }
}

4. Lock

Besides using synchronized Keyword to achieve synchronization function ,JDK It also provides Lock Interface , This way of displaying locks .

Usually we use Lock Implementation class of interface :ReentrantLock, It contains : Fair lock Not fair lock Reentrant lock Read-write lock More and more powerful functions .

for example :

public class LockService {
    private ReentrantLock reentrantLock = new ReentrantLock();
    public int age = 1;
    
    public void add(int i) {
        try {
            reentrantLock.lock();
            age = age + i;           
            System.out.println("age:" + age);
        } finally {
            reentrantLock.unlock();        
        }    
   }
}

But if ReentrantLock, It also brings a small problem that : Need to be in finally Manually release the lock in the code block .

But to be honest , In the use of Lock Show the way the lock , Solving thread safety problems , Gives developers more flexibility .

5. Distributed lock

If it is in the case of a single machine , Use synchronized and Lock There is no problem with thread safety .

But in a distributed environment , That is, if an application deploys multiple nodes , Each node can use synchronized and Lock Ensure thread safety , But between different nodes , There is no way to guarantee thread safety .

That's what you need to use : Distributed lock 了 .

There are many kinds of distributed locks , such as : Database distributed lock ,zookeeper Distributed lock ,redis Distributed locks, etc .

I personally recommend redis Distributed lock , Its efficiency is relatively higher .

Use redis The pseudo code of distributed lock is as follows :

try{
  String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);
  if ("OK".equals(result)) {
      return true;
  }
  return false;
} finally {
    unlock(lockKey);
}  

It also needs to be in finally Release lock in code block .

If you are right about redis Usage of distributed locks and common pitfalls , If you are more interested , Take a look at my other article 《 Chat redis Distributed locked 8 hole 》, There is a more detailed introduction .

6. volatile

occasionally , We have such a need : If in multiple threads , There is any thread , Set the state of a switch to false, Then the whole function stops .

After a simple requirement analysis, it is found that : Just ask for the number of threads visibility , Does not require Atomicity .

If a thread changes its state , All other threads can get the latest status value .

With such an analysis, it will be easy to do , Use volatile Can quickly meet the needs .

for example :

@Service
public CanalService {
    private volatile boolean running = false;
    private Thread thread;

    @Autowired
    private CanalConnector canalConnector;
    
    public void handle() {
        // Connect canal
        while(running) {
           // Business processing 
        }
    }
    
    public void start() {
       thread = new Thread(this::handle, "name");
       running = true;
       thread.start();
    }
    
    public void stop() {
       if(!running) {
          return;
       }
       running = false;
    }
}

What needs special attention is :volatile It cannot be used in business scenarios such as counting and statistics . because volatile The atomicity of the operation cannot be guaranteed , May cause data exceptions .

7. ThreadLocal

In addition to the above solutions ,JDK It also provides another way to use Space for time New ideas :ThreadLocal.

Of course ThreadLocal It does not completely replace locks , Especially in some seckill update inventory , A lock must be used .

ThreadLocal The core idea of : Shared variables have a copy in each thread , Each thread operates its own copy , No effect on other threads .

A warm reminder : We usually use ThreadLocal when , If after use , Be sure to remember in finally Block of code , Call it the remove Method to clear the data , Otherwise, there may be Memory leak problem .

for example :

public class ThreadLocalService {
    private ThreadLocal threadLocal = new ThreadLocal<>();

 public void add(int i) {
 Integer integer = threadLocal.get();
 threadLocal.set(integer == null ? 0 : integer + i);
 }
}

If the ThreadLocal Interested partners , Take a look at my other article 《ThreadLocal Life taking 11 Continuous questioning 》, There's right in it ThreadLocal Principle 、 Usage and pit , There is a very detailed introduction .

8. Thread safe collection

occasionally , The public resources we need to use are placed in a collection , such as :ArrayList、HashMap、HashSet etc. .

If in a multithreaded environment , Wired programs write data to these sets , Another thread reads data from the collection , There may be thread safety problems .

To solve the thread safety problem of collections ,JDK It provides us with thread safe collections .

such as :CopyOnWriteArrayList、ConcurrentHashMap、CopyOnWriteArraySet、ArrayBlockingQueue wait .

for example :

public class HashMapTest {

    private static ConcurrentHashMap hashMap = new ConcurrentHashMap<>();

 public static void main(String[] args) {

 new Thread(new Runnable() {
 @Override
 public void run() {
 hashMap.put("key1", "value1");
 }
 }).start();

 new Thread(new Runnable() {
 @Override
 public void run() {
 hashMap.put("key2", "value2");
 }
 }).start();

 try {
 Thread.sleep(50);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(hashMap);
 }
}

stay JDK Bottom , perhaps spring In the frame , Use ConcurrentHashMap There are many scenarios where loading configuration parameters are saved .

What is more famous is spring Of refresh In the method , Will read the configuration file , Put the configuration into many ConcurrentHashMap cached .

9. CAS

JDK In addition to using the lock mechanism to solve the data security problem in the case of multithreading , It also provides CAS Mechanism .

This mechanism uses CPU Compare and exchange atomicity of instructions in ,JDK It's through Unsafe Class implements the .

CAS There are four values inside : Old data Expect data The new data and Address , Compare old data and Expected data , If it's the same , Change old data into new data . If it's not the same , The current thread is constantly The spin , Until we succeed .

however , Use CAS Ensure thread safety , There may be ABA problem , Need to use AtomicStampedReference Add the version number to solve the problem .

Actually , It is seldom used directly in practical work Unsafe Class , It's usually used atomic The classes under the package are sufficient .

public class AtomicService {
    private AtomicInteger atomicInteger = new AtomicInteger();
    
    public int add(int i) {
        return atomicInteger.getAndAdd(i);
    }
}

10. Data isolation

occasionally , When we operate on set data , Can pass Data isolation , To ensure thread safety .

for example :

public class ThreadPoolTest {

    public static void main(String[] args) {

      ExecutorService threadPool = new ThreadPoolExecutor(8, //corePoolSize Number of core threads in the thread pool 
      10, //maximumPoolSize  The maximum number of threads in the thread pool 
      60, // Maximum idle time of a thread in a thread pool , After this time, idle threads will be recycled 
      TimeUnit.SECONDS,// Time unit 
      new ArrayBlockingQueue(500), // queue 
      new ThreadPoolExecutor.CallerRunsPolicy()); // Refusal strategy 

      List userList = Lists.newArrayList(
 new User(1L, " Su three ", 18, " Chengdu "),
 new User(2L, " Su San said technology ", 20, " sichuan "),
 new User(3L, " technology ", 25, " yunnan "));

 for (User user : userList) {
 threadPool.submit(new Work(user));
 }

 try {
 Thread.sleep(100);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(userList);
 }

 static class Work implements Runnable {
 private User user;

 public Work(User user) {
 this.user = user;
 }

 @Override
 public void run() {
 user.setName(user.getName() + " test ");
 }
 }
}

In this case , Use Thread pool Handle user information .

Each user is only Thread pool One of them Threads Handle , There is no case where multiple threads process a user at the same time . So this artificial data isolation mechanism , It can also ensure thread safety .

There is another scenario for data isolation :kafka The producer sends the same order message , Send to the same partion in . every last partion Deploy a consumer , stay kafka Among consumers , Use a single thread to receive messages , And do business processing .

In this case , On the whole , Different partion It uses multithreading to process data , But the same partion Is handled by a single thread , So it can also solve the thread safety problem .

If you are right about kafka I am more interested in the use of , Take a look at my other dry goods article 《 I use kafka Some extraordinary pits I stepped on in two years 》.

And finally ( Please pay attention to , Don't go whoring me for nothing )

If this article is helpful to you , Or if there's some inspiration , Help scan and issue QR code, pay attention to , Your support is my biggest motivation for writing .

Ask for one key and three links : give the thumbs-up 、 forward 、 Looking at .

Official account :【 Su San said technology 】, Reply in official account : interview 、 Code artifact 、 Development Manual 、 Time management has great fan benefits , Additional reply : Add group , Can follow a lot of BAT Exchange and study among the elders of big factories .

原网站

版权声明
本文为[pythonxxoo]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/160/202206090433192266.html