Hello everyone, I have
方圆
checked the missing knowledge and made up the missing knowledge.
table of Contents
1. synchronized
It will happen when an exception occurs自动释放锁
2. Did you know that Git is optimistic locking?
When we push to the remote warehouse, git will check whether the version of the remote warehouse is higher than ours now 版本
. If it is, it means that someone has modified the remote code and we cannot submit it, and need to synchronize first. ; If they are 版本
consistent, the submission can be successful
2.1 Implement optimistic locking in MySQL
We need to create a separate version
column to mark the version number so that every time we modify the data, the version number will be verified
The SQL statement is as follows
UPDATE active SET num = 3 ,`version` = `version` + 1 WHERE id = 1 AND `version` = 1
2.2 Applicable scenarios of optimistic locking
Optimistic lock: suitable for 读多 写少
scenarios where no lock can greatly improve efficiency.
Pessimistic lock: suitable 大并发
, 代码复杂
or循环量大
3. Fair lock and unfair lock
- Fair lock:
不允许插队
Yes, whoever comes first will get the lock first - Unfair lock:
允许插队
Yes, this is possible提高效率
,避免了唤醒带来的空档期
and it is also the default strategy of ReentrantLock
3.1 Come and see this special case
- There is one of ReentrantLock
tryLock方法
, which is really powerful. It does not不遵守我们既定的公平策略
, how can I say, that once a thread releases the locked resource, then this执行了tryLock方法的线程就能获取到锁
, even if there is already a thread queued in the front, it is just like a VIP
4. Queue jump strategy for read-write lock
- Fair lock: no jump in line is allowed
- Unfair lock:
写锁
You can jump读锁
in the queue at any time; you can jump in the queue only when the head node of想要获取读锁
the blocking queue is the thread that you want to acquire; when the node in the blocking queue is the thread that you want to acquire写锁
, then不能进行插队
this can happen避免发生饥饿
(starvation means acquiring the write lock is always Jump in the queue and never get the lock)
4.1 Code test
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadAndWriteDemo2 {
private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
private static ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
private static ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
public static void read() {
System.out.println(Thread.currentThread().getName() + "尝试获取读锁");
readLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了读锁");
TimeUnit.MILLISECONDS.sleep(80);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readLock.unlock();
}
}
public static void write() {
System.out.println(Thread.currentThread().getName() + "尝试获取写锁");
writeLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了写锁");
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放了写锁");
}
}
public static void main(String[] args) {
new Thread(() -> write(),"A").start();
new Thread(() -> read(),"B").start();
new Thread(() -> read(),"C").start();
new Thread(() -> {
for (int i = 0; i < 1500; i++) {
new Thread(() -> read(),String.valueOf(i)).start();
}
}).start();
}
}
- In
非公平
the case of ↓
- In
公平
the case of ↓
5. Write a deadlock
5.1 Code
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class DeadLockDemo2 {
public static void main(String[] args) throws InterruptedException {
DeadLock deadLock = new DeadLock(1);
new Thread(deadLock,"1").start();
TimeUnit.MILLISECONDS.sleep(1);
deadLock.setFlag(2);
new Thread(deadLock,"2").start();
}
}
class DeadLock implements Runnable{
private ReentrantLock lock1 = new ReentrantLock();
private ReentrantLock lock2 = new ReentrantLock();
private int flag;
public DeadLock(int flag) {
this.flag = flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
//TODO tryLock避免死锁式,注意tryLock方法的使用位置,是在if条件中
@Override
public void run() {
if(flag == 1) {
if(lock1.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "成功获取到了锁1");
TimeUnit.SECONDS.sleep(1);
if(lock2.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "成功获取到了锁2");
}finally {
lock2.unlock();
}
}else {
System.out.println(Thread.currentThread().getName() + "获取锁2失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock1.unlock();
}
}else {
System.out.println(Thread.currentThread().getName() + "获取锁1失败");
}
}else {
if(lock2.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "成功获取到了锁2");
TimeUnit.SECONDS.sleep(1);
if(lock1.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "成功获取到了锁1");
}finally {
lock1.unlock();
}
}else {
System.out.println(Thread.currentThread().getName() + "获取锁1失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock2.unlock();
}
}else {
System.out.println(Thread.currentThread().getName() + "获取锁2失败");
}
}
}
//TODO 死锁式
/*@Override
public void run() {
if(flag == 1) {
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁1");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "尝试获取锁2");
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁2");
}finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
}else {
lock2.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁2");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName() + "尝试获取锁1");
lock1.lock();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁1");
}finally {
lock1.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
}
}*/
}
5.2 Summary
In the above code, two methods are provided. One is 死锁
the situation that will occur , which is called lock方法
; the other is 不会发生死锁
the situation, that is, when it is used tryLock方法
, this method can try to acquire the lock, and it will automatically give up if the lock is not acquired. Of course we You can also specify the waiting time. At the same time, we must pay attention to the use position of tryLock. It is in the if条件语句
middle , which is somewhat different from the lock method.
If a deadlock occurs, we have a solution,
usejps -lThe command displays the information of the process, what we want is the process number, and
then according to the process number we usejstack process ID We can view the stack information
6. Spin lock
Spin lock: Blocking or waking up a Java thread requires the CPU to switch state to complete, which takes time. If our code is too simple, 切换状态消耗的时间可能会比用户代码执行的时间还要长
in order to avoid this situation, a spin lock is born as needed, and it will let the thread Perform continuous spin until the lock is acquired, avoiding the overhead of changing threads first (but the JVM will optimize the spin lock, there is a threshold for the number of spins, after which it will stop spinning)
atomic包
. The above are all implementations of spin locks, such as AtomicInteger. Its principle is CAS. Its getAndIncrement方法
(increment) is the spin lock used. Called Unsafe的getAndAddInt方法
as follows, it stops until the modification is successful.
-
Limitations:
If the spin lock is occupied for too long, then the spinning thread will only waste processor resources in vain -
Applicable scenarios
When the concurrency is not particularly high, and the code block is relatively simple
6.1 Handwriting a simple spin lock
public class SpinLockDemo {
private AtomicReference<Thread> spinLock = new AtomicReference<>();
public void lock() {
Thread cur = Thread.currentThread();
while(spinLock.compareAndSet(null,cur)) {
}
}
public void unLock() {
Thread cur = Thread.currentThread();
spinLock.compareAndSet(cur,null);
}
}
Come on!