##1. Basic features
1. It starts with optimistic locking. If lock conflicts are frequent, it will be converted to pessimistic locking.
2. Start with a lightweight lock implementation. If the lock is held for a long time, it will be converted into a heavyweight lock.
3. Spin locks are most likely to be used when implementing lightweight locks. Strategy
4. It is an unfair lock
5. It is a reentrant lock
6. It is not a read-write lock
2. Locking process
JVM divides synchronized locks into lock-free, biased lock, lightweight lock, and heavyweight lock states. It will be upgraded sequentially according to the situation.
Biased lock
Assume that 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 Even if the protagonist does not get a marriage certificate (avoiding high-cost operations), he can still live happily. However, the female protagonist appears and tries to compete for the male protagonist. At this time, no matter how expensive the operation of obtaining a marriage certificate is, the heroine must also This action is completed, let the female protagonist give up
The biased lock is not really "locking", it just makes a "biased lock mark" in the object header to record which thread the lock belongs to. If there is no other follow-up threads to compete for the lock, then there is no need to perform other synchronization operations (avoiding the overhead of locking and unlocking). If other threads compete for the lock later (it has just been recorded in the lock object which thread the current lock belongs to, it is easy Identify whether the thread currently applying for the lock is the previously recorded thread), then cancel the original biased lock state and enter the general lightweight lock state
The biased lock is essentially equivalent to "delayed locking". Can If you don’t lock, don’t lock. Try to avoid unnecessary locking overhead. However, you still have to do the markings, otherwise it will be impossible to distinguish when real locking is needed.
Biased locks are not real locks , but only records a mark in the object header of the lock (recording the thread to which the lock belongs). If no other threads participate in competing for the lock, then the locking operation will not actually be performed, thus reducing program overhead. Once other threads are really involved Thread competition, then cancel the biased lock state, enter the lightweight lock state
Lightweight lock
As other threads enter the competition, the biased lock state is eliminated, enter the lightweight lock state Status (adaptive spin lock). The lightweight lock here is implemented through CAS.
Check and update a piece of memory through CAS (such as null => referenced by this thread)
If the update is successful, the lock is considered to be successful.
If the update fails, the lock is considered to be occupied, and the spin wait is continued (without giving up the CPU).
The spin operation is Keeping the CPU idling is a waste of CPU resources. Therefore, the spin here will not continue forever, but will stop spinning after reaching a certain time/number of retries. This is the so-called "adaptive"
Heavyweight lock
If the competition becomes more intense and the spin cannot obtain the lock status quickly, it will expand into a heavyweight lock. The heavyweight lock here refers to the use of the mutex provided by the kernel.
To perform the locking operation, first enter the kernel state.
Determine whether the current lock has been occupied in the kernel state
If the lock is not occupied, the locking is successful and the switch Return to user mode.
If the lock is occupied, the lock fails. At this time, the thread enters the waiting queue of the lock and hangs. Waiting to be awakened by the operating system.
Goed through a series of Over time, the lock was released by other threads, and the operating system also remembered the suspended thread, so it woke up the thread and tried to reacquire the lock
3. Other optimization operations
Lock elimination
The compiler JVM determines whether the lock can be eliminated. If it can, it will be eliminated directly
Some applications use synchronized in their code, but they are not actually used in a multi-threaded environment (such as StringBuffer) )
StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");
sb.append("d");
At this time, each append call will involve locking and unlocking. But if this code is only executed in a single thread, then these locking and unlocking operations are not necessary, and some resource overhead is wasted. .
Lock coarsening
If multiple locks and unlocks occur in a piece of logic, the compiler JVM will automatically coarsen the lock.
Leader, explain work tasks to subordinates
Method 1:
Make a phone call, explain task 1, hang up the phone.
Make a phone call, explain task 2, hang up the phone .
Make a phone call, give task 3, hang up the phone
Method 2:
Make a phone call, give task 1, task 2, task 3, hang up the phone
4. Callable interface
What is Callable
Callable is an interface. It is equivalent to encapsulating a "return value" in a thread. It is convenient for programmers to calculate results using multi-threading.
Callable 和 Runnable 相对, 都是描述一个 "任务". Callable 描述的是带有返回值的任务, Runnable 描述的是不带返回值的任务.Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为 Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定. FutureTask 就可以负责这个等待结果出来的工作.
代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本
public class Text {
static class Result{
public int sum = 0;
public Object locker = new Object();
}
public static void main(String[] args) throws InterruptedException {
Result result = new Result();
Thread t = new Thread(){
@Override
public void run() {
int sum = 0;
for (int i = 0; i <=10000; i++){
sum += i;
}
result.sum = sum;
synchronized (result.locker){
result.locker.notify();
}
}
};
t.start();
synchronized (result.locker){
while (result.sum == 0){
result.locker.wait();
}
}
System.out.println(result.sum);
}
}
代码示例: 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Text1 {
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 = 0; i <=1000; i++){
sum += i;
}
return sum;
}
};
//由于Thread不能直接传一个callable实例,就需要一个辅助类来包装
FutureTask<Integer> futureTask = new FutureTask<>(callable);
Thread t = new Thread(futureTask);
t.start();
//尝试在主线程获取结果
//如果FutureTask中的结果还没生成。此时就会阻塞等待
//一直等到最终的线程把这个结果算出来,get返回
Integer result = futureTask.get();
System.out.println(result);
}
}
The above is the detailed content of The principles and usage scenarios of Synchronized in Java and the usage and difference analysis of the Callable interface. For more information, please follow other related articles on the PHP Chinese website!