Moment For Technology

【 study notes 】 Atomicity of threads - use of synchronized

Posted on June 24, 2022, 1:26 a.m. by Kay Waters
Category: The back-end Tag: The back-end

Definition of thread safety:

Is a class thread-safe when it is accessed by multiple threads, regardless of how the runtime environment is scheduled or how the processes are executed interchangeably, and does not require any additional synchronization or coordination in the calling code

There are two types of atomic locks:

Synchronized: is a Java keyword that is a type of synchronization lock that depends on the JVM
Lock: Rely on special CPU instructions, code implementation, ReentrantLock

Synchronized

2. Modify method: entire method, applied to the called object. 3. Modify static method: entire static method, applied to all objects

Modify code block

package com.lyy.concurrency.sync;



import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SynchronizedExample1 {

    // Decorates a code block
    public void test1(int j){
        synchronized (this) {for (int i = 0; i  10; i++) {
                System.out.println("Test1 j."+j+" — i:"+i); }}}public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
        // Add the thread pool and we call two threads
        // Two threads called the same object
        executorService.execute(() -{
            example1.test1(1);
        });
        executorService.execute(() -{
            example2.test1(2); }); }}Copy the code

Return result:

Test1 j:1- I:0Test1 j:1- I:1Test1 j:1- I:2Test1 j:1- I:3Test1 j:1- I:4Test1 j:1- I:5Test1 j:1- I:6Test1 j:1- I:7Test1 j:1- I:8Test1 j:1- I:9Test1 j:2- I:0Test1 j:2- I:1Test1 j:2- I:2Test1 j:2- I:3Test1 j:2- I:4Test1 j:2- I:5Test1 j:2- I:6Test1 j:2- I:7Test1 j:2- I:8Test1 j:2- I:9
Copy the code

Thread 1 and thread 2 execute in their own order. Thread 1 and thread 2 can follow their own synchronized code, but there is no guarantee that thread 1 will execute until thread 2 executes, depending on which thread can grab the resources first.

Modification methods

package com.lyy.concurrency.sync;



import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SynchronizedExample1 {



    // Decorates a method
    public synchronized void test2(int j){
        for (int i = 0; i  10; i++) {
            System.out.println("test2 j:"+j+" — i:"+i); }}public static void main(String[] args) {
        SynchronizedExample1 example1 = new SynchronizedExample1();
        SynchronizedExample1 example2 = new SynchronizedExample1();
        ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
        // Add the thread pool and we call two threads
        // Two threads called the same object
        executorService.execute(() -{
            example1.test2(1);
        });
        executorService.execute(() -{
            example2.test2(2); }); }}Copy the code

Return result:

Test2 j:1- I:0Test2 j:1- I:1Test2 j:1- I:2Test2 j:1- I:3Test2 j:1- I:4Test2 j:1- I:5Test2 j:1- I:6Test2 j:1- I:7Test2 j:2- I:0Test2 j:2- I:1Test2 j:2- I:2Test2 j:2- I:3Test2 j:2- I:4Test2 j:2- I:5Test2 j:2- I:6Test2 j:2- I:7Test2 j:2- I:8Test2 j:1- I:8Test2 j:2- I:9Test2 j:1- I:9
Copy the code

We can see that 1 and 2 are running interchangeably, but each is executing in order, because the modifier block only works on the object that's currently being called, and we're calling two methods so the two threads are not interfering with each other, each executing its own code, and synchronization is the whole method

Note: If SynchronizedExample1 were a subclass, test2 would not implement synchronized because synchronized is not part of the method declaration, and therefore cannot be inherited. To implement this subclass inheriting synchronized, you need to implement it manually

decorator

package com.lyy.concurrency.sync;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SynchronizedExample2 {

    // Decorates a class
    public static void test1(int j){
        synchronized (SynchronizedExample2.class){
            for (int i = 0; i  10; i++) {
                System.out.println("Test1 j."+j+" — i:"+i); }}}public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
        // Adding the thread pool equals calling two threads L
        // Two threads called the same object
        executorService.execute(() -{
            example1.test1(1);
        });
        executorService.execute(() -{
            example2.test1(2); }); }}Copy the code

Return result:

Test1 j:1- I:0Test1 j:1- I:1Test1 j:1- I:2Test1 j:1- I:3Test1 j:1- I:4Test1 j:1- I:5Test1 j:1- I:6Test1 j:1- I:7Test1 j:1- I:8Test1 j:1- I:9Test1 j:2- I:0Test1 j:2- I:1Test1 j:2- I:2Test1 j:2- I:3Test1 j:2- I:4Test1 j:2- I:5Test1 j:2- I:6Test1 j:2- I:7Test1 j:2- I:8Test1 j:2- I:9
Copy the code

Decorated static methods

package com.lyy.concurrency.sync;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SynchronizedExample2 {



    // Decorate a static method
    public static  synchronized void test2(int j){
        for (int i = 0; i  10; i++) {
            System.out.println("test2 j:"+j+" — i:"+i); }}public static void main(String[] args) {
        SynchronizedExample2 example1 = new SynchronizedExample2();
        SynchronizedExample2 example2 = new SynchronizedExample2();
        ExecutorService executorService = Executors.newCachedThreadPool();// Declare a thread pool
        // Adding the thread pool equals calling two threads L
        // Two threads called the same object
        executorService.execute(() -{
            example1.test2(1);
        });
        executorService.execute(() -{
            example2.test2(2); }); }}Copy the code

Return result:

Test2 j:1- I:0Test2 j:1- I:1Test2 j:1- I:2Test2 j:1- I:3Test2 j:1- I:4Test2 j:1- I:5Test2 j:1- I:6Test2 j:1- I:7Test2 j:1- I:8Test2 j:1- I:9Test2 j:2- I:0Test2 j:2- I:1Test2 j:2- I:2Test2 j:2- I:3Test2 j:2- I:4Test2 j:2- I:5Test2 j:2- I:6Test2 j:2- I:7Test2 j:2- I:8Test2 j:2- I:9
Copy the code

Case study:

Thread unsafe cases:

package com.lyy.concurrency.example.count;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;



public class CountExample1 {

    // Total requests
    public static int clientTotal = 5000;
// The number of concurrent threads
    public static int threadTotal = 200;

    //
    public static int count = 0;

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();/ / thread pool
        final Semaphore semaphore = new Semaphore(threadTotal);// The number of concurrent requests allowed
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i  clientTotal; i++) {
                executorService.execute(() -{
                    try {
                        semaphore.acquire();// Determine whether the thread is allowed to execute
                        add();// Acquire () will not be executed until it returns a value
                        semaphore.release();
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("count:"+count);
    }

    private static void add(a){ count++; }}Copy the code

Execution Result:

The count:4973
Copy the code

The correct result is 4973, and the correct result is 5000. How can we get the result to 5000 by using synchronized for a thread-safe class

Thread-safe classes:

package com.lyy.concurrency.example.count;

import com.lyy.concurrency.annoatioons.NotThreadSafe;
import com.lyy.concurrency.annoatioons.ThreadSafe;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

@Slf4j
@ThreadSafe // Thread-safe classes
public class CountExample3 {

    // Total requests
    public static int clientTotal = 5000;
// The number of concurrent threads
    public static int threadTotal = 200;

    //
    public static int count = 0;

    public static void main(String[] args) throws Exception{
        ExecutorService executorService = Executors.newCachedThreadPool();/ / thread pool
        final Semaphore semaphore = new Semaphore(threadTotal);// The number of concurrent requests allowed
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i  clientTotal; i++) {
                executorService.execute(() -{
                    try {
                        semaphore.acquire();// Determine whether the thread is allowed to execute
                        add();// Acquire () will not be executed until it returns a value
                        semaphore.release();
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("count:"+count);
    }

    private synchronized static void add(a){ count++; }}Copy the code

Here we add synchronized to make our results show the correct 5000

Return result:

The count:5000
Copy the code

Summary: 1, synchronized: is an uninterruptible lock, suitable for the competition is not fierce, readable 2, whether synchronized keyword added to the method or object, if it acts on the object is not static, then it obtains the lock is the object; 3. If synchronized acts on a static method or a class, it acquires the same lock for all objects of that class. 4. Each object has only a lock associated with it, and whoever holds this lock can run the code it controls. 5. Synchronization is expensive and can even cause deadlocks, so avoid unnecessary synchronization control.

Search
About
mo4tech.com (Moment For Technology) is a global community with thousands techies from across the global hang out!Passionate technologists, be it gadget freaks, tech enthusiasts, coders, technopreneurs, or CIOs, you would find them all here.