Talk about JMM, threads, thread pools. All for distributed action!

JMM

  • Understand the diagram below

Java files, you compile them, you program class files through Java compilation, class files become bytecode, load them into the class loader, and execute them through the class loader, and one of the models of that execution is shown here.

  • features

1. The visibility

Visibility means that when one thread makes a change to a shared variable, other threads are immediately aware of the change. In synchronzed memory semantics, when a thread acquises a lock, it retrieves the latest value of a shared variable from main memory and synchronizes the shared variable to main memory when the lock is released. Thus, synchronized is visible. Similarly, in volatile analysis, memory visibility is achieved by adding the LOCK instruction to the instruction. Therefore, volatile is visible.

2. The atomicity

Atomicity refers to the fact that an operation is uninterruptible, and either all of it succeeds or all of it fails, with a sense of “live or die”. Even when multiple threads are working together, an operation can be started without interference from other threads.

3. The order

Synchronized semantics indicates that a lock can be acquired by only one thread at a time. When the lock is occupied, other threads can only wait. As stated in the Java memory model, the compiler and processor perform instruction reordering for performance optimization; In other words, the natural orderlessness of Java programs can be summarized as follows: if you look inside the thread, all operations are ordered; If you observe another thread from one thread, all operations are unordered

Because the JVM to run the program of the entity is a thread, and every thread creation when the JVM to create a working memory (called the stack space) in some places, and threads used to store the private data, while the Java memory model that all variables are stored in main memory, main memory is Shared memory region, all threads can access, But threads (reading assignment, etc.) to the operation of the variables must be conducted in the working memory, first of all variables from the main memory to copy their working memory space, and operation, the variable operation to complete before you write variables back to main memory, cannot be directly operating variables in main memory, working memory storage variables in the main memory copy of a copy of the said earlier, Working memory is the private data area of each thread, so different threads cannot access each other’s working memory. Communication between threads (passing values) must be done through main memory.

4. Happens-before principle

What is a thread

Threads are an operating system concept. The operating system is responsible for creating, suspending, running, blocking, and terminating this thread. The operating system creates threads, switches thread states, and terminates threads with CPU scheduling — a time-consuming and resource-consuming process.

  • The life cycle

In Java, threads typically have five states: created, ready, running, blocked, and dead.

  1. Create state. When a thread object is generated, the object’s start method is not called, which is the thread being created.
  2. Ready state. When the start method of a thread object is called, the thread is ready, but the thread scheduler has not set the thread to the current thread. The thread is also in the ready state after it runs, after it comes back from waiting or sleeping.
  3. Running status. The thread scheduler sets the thread in the ready state to the current thread, at which point the thread enters the run state and starts running the code in the run function.
  4. Blocked status. A thread is suspended while it is running, usually to wait for a certain time to occur (such as when a resource is ready) before continuing. Methods like sleep,suspend, and wait can all cause threads to block.
  5. State of death. If a thread’s run method finishes or stops, the thread dies. For dead threads, you can no longer use the start method to get them ready.

Jstack or IDEA Debug snapshots can be used to display the status

  1. The “Low Memory Detector” is responsible for detecting the available Memory. If the available Memory is found to be Low, it allocates new Memory space.
  2. “CompilerThread0” is used to call JITing to compile and unload classes in real time.
  3. “Signal Dispatcher” is responsible for distributing internal events.
  4. “Finalizer” is responsible for calling the Finalizer method.
  5. The “Reference Handler” handles references.
  6. “Main” is the main thread.
  7. “VM Thread”, “VM Periodic Task Thread” is an internal Thread of the virtual machine.
  • State description
  1. The NEW state indicates that the thread has just been created and has not yet been started
  2. A RUNNABLE state is one in which a thread is running normally, but there may be some kind of computation /I/O wait /CPU time chip switch, etc. The wait in this state is usually for other system resources, but not for locks, sleeps, etc
  3. BLOCKED is a state where multiple threads have synchronized operations, such as a synchronized block that is awaiting release from another thread, or a reentrant synchronized block that calls wait(), where the thread is waiting to enter a critical section
  4. WAITING is when a thread holding a lock calls its wait method and waits for notify/notifyAll to be called by another thread/lock owner. The thread is BLOCKED and WATING. One is WAITING for entry outside the critical point, and the other is WAITING for notify inside the understanding point. When the thread calls the join method and joins another thread, it will also enter the WAITING state, WAITING for the thread that is joined to execute the junction
  5. Wait (long), join(long), wait(long), wait(long), wait(long), wait(long), wait(long) It enters TIMED_WAITING state. This state indicates that the thread’s run method is TERMINATED and is essentially dead. (At the time, threads that are persistent may not be recycled.)
  • Priority Priority

The priority of a thread passes the importance of that thread to the scheduler. There is some uncertainty about the order in which the CPU processes threads, but the scheduler tends to execute first with higher priority

  • implementation

Thread, Runnable, and Callable

The difference between Runnable and Thread: 1, there is no difference in effect, the difference in writing method is only a difference. 2, There is no comparison. Thread implements the Runnable interface and extends it. We usually compare Thread and Runnable only in terms of writing. 3. Callable1.5 introduced, has return values, supports generics, and is no longer a run method but a call method.

  • Why use thread pools when you have threads

Thread switching is time-consuming, and the thread pool is more efficient by pulling threads directly from the management pool.

Executor framework architecture

The Executor in java.util.Concurrent manages our previous Thread threads. The goal is to simplify concurrent programming.

ScheduledThreadPoolExecutor and ThreadPoolExecutor.

Constructors: number of cores, task queue container, lifetime, thread factory, processor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          longKeepAliveTime,// TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {/ / hander

Copy the code
  • LinkedBlockingQueue

  • Execute (submit) method – > Java. Util. Concurrent. ThreadPoolExecutor# Execute

If the number of threads in the pool is less than corePoolSize, addWorker is called to add a thread to execute the task. Otherwise, the workqueue.offer (command) method is used to enter the column. The number of threads in the pool will be determined at one time if the pool is successfully joined. Since we may require zero core threads to create the pool, we must use addWorker(null, false) to create a temporary thread to block the queue for the task to execute.

if (command == null)
    throw new NullPointerException();
int c = ctl.get();
// Check whether the number is smaller than the number of cores
if (workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
        return;
    c = ctl.get();// Continue to get tokens after the increment fails
}
// Determine the running status and throw it to the workQueue successfully
if (isRunning(c) && workQueue.offer(command)) {
    int recheck = ctl.get();
// Check the running status again. If it is not running, remove it &reject
    if (! isRunning(recheck) && remove(command))
        reject(command);
    else if (workerCountOf(recheck) == 0) // Add a null worker if the number of threads running is 0.
        addWorker(null.false);
}
else if(! addWorker(command,false)) // Add worker directly. If not, reject
    reject(command);


Copy the code
  • AddWorker method

Conclusion:

If firstTask is not null, the created thread will execute the firstTask object and then block the task from the queue. No Directly obtain the task from the blocking queue. If the second argument, core, is true, corePoolSize is used as the maximum number of threads in the pool. If false, maximumPoolSize is used as the maximum number of threads in the pool. The t.start method run calls the runWorker method

retry:
for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);

    // Check if queue empty only if necessary. 
    if (rs >= SHUTDOWN &&
        ! (rs == SHUTDOWN &&
           firstTask == null&&! workQueue.isEmpty()))return false;// If the workQueue is not null, the workQueue is not null.

    for (;;) {
        int wc = workerCountOf(c);
        if (wc >= CAPACITY ||
            wc >= (core ? corePoolSize : maximumPoolSize))
            return false;// Check whether the capacity is full
        if (compareAndIncrementWorkerCount(c)) // Increase the number of works and jump out
            break retry;
        c = ctl.get();  // Continue recursion after work fails
        if(runStateOf(c) ! = rs)continue retry;
        // else CAS failed due to workerCount change; retry inner loop}}boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
    w = new Worker(firstTask);// Add a worker
    final Thread t = w.thread;
    if(t ! =null) {// Check whether it is null
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // Recheck while holding lock.
            // Back out on ThreadFactory failure or if
            // shut down before lock acquired. After the lock, recheck for thread factory failures or pre-lock shutdowns
            int rs = runStateOf(ctl.get());

            if (rs < SHUTDOWN ||
                (rs == SHUTDOWN && firstTask == null)) {
                if (t.isAlive()) // precheck that t is startable
                    throw new IllegalThreadStateException();  
                workers.add(w);   / / add the work
                int s = workers.size();
                if (s > largestPoolSize)
                    largestPoolSize = s;
                workerAdded = true; }}finally {
            mainLock.unlock();
        }
        if (workerAdded) { // If the new work is added successfully, call start to run
            t.start();
            workerStarted = true; }}}finally {
    if (! workerStarted)
        addWorkerFailed(w);
}
return workerStarted;


Copy the code
  • runWorker

Summary: Get the task and call the thread run method

Thread wt = Thread.currentThread();//1. Fetch the current thread
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
    while(task ! =null|| (task = getTask()) ! =null) { // Get quests to see if you can get points to enterA queue poll take in a Task actually gets the Task//workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :Fetch w.lock();// If pool is stopping, ensure thread is interrupted;
        // if not, ensure thread is not interrupted. This
        // requires a recheck in second case to deal with
        // shutdownNow race while clearing interrupt
        if((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && ! wt.isInterrupted()) wt.interrupt();// Make sure the thread is interruptible
        try {
            beforeExecute(wt, task); // The hook before the task begins
            Throwable thrown = null;
            try {
                task.run();// Execute the task
            } catch (RuntimeException x) {
                thrown = x; throw x;
            } catch (Error x) {
                thrown = x; throw x;
            } catch (Throwable x) {
                thrown = x; throw new Error(x);
            } finally {
                afterExecute(task, thrown); // The hook after the task}}finally {
            task = null;
            w.completedTasks++;
            w.unlock();
        }
    }
    completedAbruptly = false;
} finally {
    processWorkerExit(w, completedAbruptly); / / look here
}

Copy the code
  • processWorkerExit
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
    decrementWorkerCount();

final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
    completedTaskCount += w.completedTasks;
    workers.remove(w);  / / remove the work
} finally {
    mainLock.unlock();
}

tryTerminate();

int c = ctl.get();
if (runStateLessThan(c, STOP)) { // Determine if there are still tasks
    if(! completedAbruptly) {int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
        if (min == 0&&! workQueue.isEmpty()) min =1;
        if (workerCountOf(c) >= min)
            return; // replacement not needed
    }
    addWorker(null.false);

Copy the code

Thread pool rejection policy

The default of the four policies is AbortPolicy

The name of the meaning
AbortPolicy (default) When a task is added to the thread pool is refused, it will throw RejectedExecutionException anomalies.
CallerRunsPolicy When a task is rejected when added to the Thread pool, the rejected task is processed in the Thread pool that is currently running in the Thread pool.
DiscardOldestPolicy When a task added to the thread pool is rejected, the thread pool discards the oldest unprocessed task in the wait queue, and then adds the rejected task to the wait queue
DiscardPolicy When a task added to the thread pool is rejected, the thread pool discards the rejected task.
  • The realization of the ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor inherited ThreadPoolExecutor and implements ScheduledExecutorService interface. ScheduledExecutorService defines the interface using the ScheduledThreadPoolExecutor submit task schedule. Schedule is a set of Overwrite interfaces that return different values and parameters. So that the user can choose the mode of submitting the task, such as one-time scheduled task or periodic task, whether to return results and so on. Regardless of the schedule interface, its implementation mainly encapsulates user-submitted tasks and patterns into ScheduledFutureTask and then invokes delayedExecute(Runnable Command). So the delayedExecute method is really the way to process tasks.

PS: Concurrency is always a step by step operation based on the thread’s judgment, so one way to improve the efficiency of the server is to minimize the number of objects created and destroyed, especially resource-intensive ones. This is just for understanding. I’m at my wit’s end.