JAVA--Concurrent Thread Security Processing (1) --Thread Security

Knowledge about multithreaded concurrency and thread security is organized as follows:

  1. How Threads Ensure Security
  2. How to publish objects securely
  3. What are the means of thread security
  4. Explanation of JUC components
  5. How to improve thread scheduling

1. How do threads ensure security?

  • What is Thread Security

This class is thread safe when multiple threads access a class, regardless of how the runtime environment is scheduled or how these processes will execute alternately, and no additional synchronization or collaboration is required in the main code.

 

  • Three characteristics of thread security

Atomicity, order, visibility

Atomicity: Provides mutually exclusive access to which only one thread can operate at a time.

Orderliness: A thread observes the order in which instructions are executed in other threads. Due to the existence of instruction reordering, this observation is generally disordered.

Visibility: Changes to main memory made by one thread can be observed by other threads in a timely manner.

Characteristic operation
Atomicity synchronized blocks of code guarantee serial execution
Orderliness Can be implemented by volatile / synchronized (at most one thread can lock a variable)
visibility Can be achieved by final (no modification), volatile (forced update + read main memory), and synchronized (all modified data is refreshed to main memory when unlock, and data is reloaded from main memory when lock)

 

  • Atomic Package

Java has provided the java.util.concurrent.atomic package since JDK1.5 to facilitate programmers to perform atomic operations without locks in a multithreaded environment.The bottom level of the atomic variable uses the atomic instructions provided by the processor, but different CPU architectures may provide different atomic instructions or may require some form of internal lock, so this method does not absolutely guarantee that the thread is not blocked.

Upper Code

package Atomic;

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

//Thread Security
public class AtomicIntegerDemo {

    private final static int clientTotal=5000;//Total Threads
    private final static int threadTotal=200;//Number of threads per pass

    static AtomicInteger ai=new AtomicInteger(0);

    public static void main(String[] args) {
        //Thread Pool
        ExecutorService executorService= Executors.newCachedThreadPool();
        //Generate semaphore
        final Semaphore semaphore=new Semaphore(threadTotal);//20 passes at a time
        final CountDownLatch latch=new CountDownLatch(clientTotal);//Counter
        for(int i=0;i<clientTotal;i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();//Apply/Obtain a license
                    //incrementAndGet()
                    //-->1.unsafe.getAndAddInt
                    //-->2.this.compareAndSwapInt(CAS)
                    //-->3.native Method
                    //2->3,Actually, it is the process of checking working and main memory(CAS Of particular importance)
                    ai.incrementAndGet();//Data plus 1
                    semaphore.release();//Release License
                } catch (Exception e) {
                    System.out.println("execption:" + e);
                }
                latch.countDown();//Counter minus 1
            });
        }
        try {
           latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();//Close Threads
        System.out.println(ai.get());
    }
}

 

  • Synchronized (lock)

synchronized: Depending on the JVM, the JVM automatically locks and unlocks

Lock: Depends on CPU instructions, requires manual unlock, ReentrantLock

 

Synchroized code

  

package Sync;

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

public class SynchronizedDemo {

    //Modify a whole method
    private synchronized void test1(){
        for(int i=0;i<10;i++){
            System.out.println("Test1 - "+i);
        }
    }
    //Decorate a block of code
    private void test2(){
        synchronized (this){
            for(int i=0;i<10;i++){
                System.out.println("Test2 - "+i);
            }
        }
    }

    public static void main(String[] args) {

        SynchronizedDemo demo1=new SynchronizedDemo();
        ExecutorService executorService= Executors.newCachedThreadPool();
            executorService.execute(()->{
                demo1.test1();
            });
            executorService.execute(()->{
                demo1.test2();
            });
        executorService.shutdown();
    }
}

The corresponding scopes for Synchronized are as follows

Operation method Scope
Modify a piece of code Brace-enclosed code that acts on the called object
Modification Method Whole method, acting on the called object
Modify Static Method Whole static method, acting on all objects (considering JVM principles)
Modifier class Parts enclosed in brackets that act on all objects (from JVM principles)

 

Lock code

package Sync;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockDemo {

    private static int count=0;
    private final static int threadPoolCount=5000;
    private final static int threadCount=200;
    private static Lock lock=new ReentrantLock();
    public static void main(String[] args) {

        ExecutorService executorService= Executors.newCachedThreadPool();//Thread Pool
        Semaphore semaphore=new Semaphore(threadCount);
        final CountDownLatch countDownLatch = new CountDownLatch(threadPoolCount);

        for(int i=0;i<threadPoolCount;i++){
            executorService.execute(()->{
                try {
                    semaphore.acquire();
                     add();
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        System.out.println(count);
    }

    private static void add(){
        lock.lock();//locking
        count++;//Methods requiring synchronization
        lock.unlock();//Unlock
    }
}

 

Atomic, synchronized, Lock comparison

Atomic: Maintains normality in times of intense competition, performs better than Lock, but updates only one value

Synnized: Non-interruptable lock, suitable for less competitive, readable, JVM automatically releases resources.

Lock: Interruptible locks, which can be maintained under intense competition, require manual locking and unlocking.

  

  • visibility

Reasons why shared variables are not visible between threads (threads cross-execute, reordering combined with thread cross-execute, values updated by shared variables are not updated in working memory and main memory in a timely manner)

Visibility-synchronized

Two rules of synchronized in the JAVA memory model:

1. You must refresh the latest shared variables to main memory before the thread can unlock

2. When a thread locks, the values of shared variables in working memory are cleared, triggering the need to use shared variables and having to re-read the latest values from main memory

 

Visibility-volatile

By adding memory barriers and prohibiting reordering optimization

When a volatile variable is written, a store barrier directive is added after the write operation to refresh the shared variable values in local memory to main memory.

When a volatile variable is read, a load barrier directive is added before the read operation to read the shared variable from main memory

package Sync;

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

public class VolatileDemo {

    private static volatile int count=0;
    private final static int threadPoolCount=5000;
    private final static int threadCount=200;
    public static void main(String[] args) {

        ExecutorService executorService= Executors.newCachedThreadPool();//Thread Pool
        Semaphore semaphore=new Semaphore(threadCount);
        final CountDownLatch countDownLatch = new CountDownLatch(threadPoolCount);

        for(int i=0;i<threadPoolCount;i++){
            executorService.execute(()->{
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            });
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        System.out.println(count);
    }

    private static void add(){
        //1.First step acquisition count
        //2.Step 2+1
        //3.hold count Value, returned to main memory
        count++;
        //Calculation results show Volatile Not Atomic
        //Volatile Better use of status tags ( boolean)
        //Writing to a variable does not depend on the current value
        //The variable is not included in a variable with other variables
    }
}

 

  • Orderliness

The JAVA memory model allows compilers and processors to reorder instructions, but the reordering process does not affect the execution of single-threaded programs, but it does affect the correctness of multithreaded concurrent execution.

The Eight Principles of happens-before

Procedural Sequence Rule: Within a thread, executes in the order of code.

Lock rule: An unLock operation occurs after a lock operation for a lock.

volatile variable rule: Writing a variable first occurs after reading the variable.

Delivery rule: A action B, B action C, then A action C can be derived.

Thread Start Rule: The Thread object's start() method first sends each action with this thread.

Thread interrupt rule: A call to the thread interrupt() method sends first and the code of the interrupt thread detects an interrupt event.

Thread termination rule: All operation termination detection in a thread can be ended by the Thread.join() method.

Object Termination Rule: The initialization of an object is first sent to the beginning of its finalize() method.

Keywords: Java jvm less

Added by maya28 on Thu, 16 May 2019 03:18:15 +0300