The principle of synchronized and the Callable interface

Table of Contents

?synchronized principle

?Lock upgrade

?Lock optimization

?Callable interface


?synchronized principle

We know that synchronized locks can control multiple threads’ access to shared resources, and two threads will block and wait when accessing the same variable. The synchronized lock is not static, it will be upgraded according to the situation.

?Lock Upgrade

JVM
Synchronized locks are divided into four states: no lock → biased lock → lightweight lock → heavyweight lock.

?Bias lock

The first thread that attempts to lock will enter the biased lock state first. Biased locking is not a real lock. It just gives the thread a biased lock mark. If no other thread attempts to lock, wait until synchronized is executed to cancel the biased lock mark. If there are other threads trying to lock, then the biased lock mark will be removed. While locking, let other threads enter blocking waiting. (Similar to the lazy mode introduced earlier, if you can do it without locking, don’t add it)

For example: Suppose the male protagonist is a lock and the female protagonist is a thread. If only this thread uses this lock, then the male protagonist and the female protagonist can live happily forever even if they do not get a marriage certificate (avoiding high-cost operations). But the female protagonist appears and tries to compete with the male protagonist. No matter how high the cost of obtaining a marriage certificate is, the female protagonist must complete this action and make the female protagonist give up.

?Lightweight lock

When other threads try to lock
, the bias lock state is eliminated,
Entered lightweight lock state
(
Adaptive spin lock
). The lightweight lock here is the spin lock implemented through CAS. Since the spin lock requires the CPU to continuously try to lock, if it keeps spinning, it will waste CPU resources. Therefore, synchronized will use a counter to record the number of spins. If it exceeds a certain threshold, it will no longer spin.

?Heavyweight lock

If competition becomes more intense
,
Spin cannot quickly obtain the lock state
,
It will be converted into a heavyweight lock. The heavyweight lock here uses the mutex lock provided by the OS. At this time, when other threads try to lock, they determine whether the lock is occupied in the kernel state. If it is not occupied, it returns to the user state. Otherwise, it enters the waiting queue and waits for the operating system to wake up.

?Lock Optimization

?Lock elimination

Lock elimination means that the compiler automatically determines that certain code blocks in the program do not require synchronization protection, so it eliminates the locks in these code blocks, thereby improving the execution efficiency of the program.

The main basis for lock elimination is: if certain code blocks in the program will not be accessed by multiple threads at the same time, then the locks in these code blocks can be eliminated because no additional time is needed for synchronization operations.

For example: the key methods of StringBuffer are synchronized,
But if this method is only executed in a single thread, then these locking operations are not necessary, and the compiler will remove the locking operations.

Lock roughening

Lock coarsening refers to merging multiple consecutive fine-grained locks (synchronized contains fewer code blocks) into one coarse-grained lock (synchronized contains more code blocks) to reduce the performance overhead caused by lock competition.

In the actual development process, fine-grained locks are used in the hope that other threads can use the lock when the lock is released. But there may not actually be other threads to seize this lock. In this case, the JVM will automatically coarsen the lock to avoid frequent application and release of the lock.

for example:

Zhang San wants to call the teacher to ask three questions, call once to ask one question, and call three times in a row. Lock optimization is equivalent to Zhang San asking three questions together in one phone call.

?Callable interface

Callable
Is a
Interface, similar to Runnable, is also used to describe a task. The difference is that the task described by Runnable has no return value, while the task described by Callable has a return value. If you need a thread to calculate a certain result alone, Callable is more appropriate.

For example: create a thread to calculate 1 + 2 + 3 + … + 1000,
Do not use
Callable version:

①.Create a class
Result,
contains a
sum
represents the final result,
lock
Represents the lock object used for thread synchronization.

②.main
Create first in the method
Result
instance,
Then create a Runable, calculate 1 + 2 + 3 + … + 1000 in the Runnable, and pass the Runnable into the thread thread.

③.Use the main thread at the same time
wait
waiting thread
thread
The calculation ends.
(
Notice:
If executed to
wait
Before,
thread
thread
The calculation has been completed,
You don’t have to wait).

④.When thread
thread
After the calculation is completed,
pass
notify
Wake up the main thread,
The main thread then prints the results.

public class Test {
    static class Result {
        public int num = 0;
        public Object lock = new Object();
    }

    public static void main(String[] args) throws InterruptedException {
        Result result = new Result();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int num = 0;
                for (int i = 1; i <= 1000; i + + ) {
                    num + = i;
                }
                synchronized (result.lock) {
                    result.num = num;
                    result.lock.notify();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        synchronized (result.lock) {
            while (result.num == 0) {
                result.lock.wait();
            }
            System.out.println(result.num);
        }
    }
}

As you can see, the above code requires an auxiliary class Result and a series of locking and wait notify operations. The code is complex and error-prone.

Create thread calculation
1 + 2 + 3 + … + 1000,
use
Callable
Version:

①.Create an anonymous inner class,
accomplish
Callable
interface,
Callable
With generic parameters,
Generic parameters represent the type of return value.

②.Rewrite
Callable
of
call
method,
Complete the accumulation process,
Return the calculation result directly through the return value.

③.Hand
callable
Example usage
FutureTask
Pack it up.

④.Create a thread
,
The thread’s construction method is passed in
FutureTask.
At this point the new thread will execute
FutureTask
Internal
Callable
The call method,
Complete the calculation,
The calculation results are placed in
FutureTask
in the object.

⑤. Called in the main thread
futureTask.get()
Able to block and wait for the new thread to complete calculations,
and get
FutureTask
results in.

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 1000; i + + ) {
                    sum + = i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        int result = futureTask.get();
        System.out.println(result);
    }
}

As you can see, after using Callable and FutureTask, the code is simplified a lot, and there is no need to manually write thread synchronization code.

Note:

Callable usually needs to be used with FutureTask. FutureTask is used to save the return result of Callable. Because Callable is often executed in another thread and it is not certain when it will be executed. FutureTask can be responsible for the work of waiting for the result.

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Java Skill TreeHomepageOverview 139331 people are learning the system