Java中关于各种锁的概念
乐观锁:
是一种乐观思想,认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作
当线程拿到资源时,上乐观锁,在提交前,其他的锁也可以操作这个资源,当冲突的时候,并发机制会保留前一个提交,打回后一个提交,让后一个线程重新获取资源后,再操作,再提交。
拿版本号去对比
lock是乐观锁
悲观锁:
认为读少写多,遇到并发写的可能性高,每次拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,别人想拿到这个数据就会block,直到拿到锁。
当线程拿到资源时,就对资源上锁,并在提交后,才释放资源,其他线程才能使用资源
synchronized是悲观锁
在并发量低的时候性能差不多,在并发量高的时候,乐观锁的性能远远优于悲观锁。
公平锁、非公平锁
公平锁:非常公平,不能插队,必须先来后到
非公平锁:可以插队,非常不公平(默认都是非公平锁)
Lock lock = new ReentrantLock(); //非公平锁
Lock lock = new ReentrantLock(true); //公平锁
可重入锁
递归锁:拿到外面的锁也就拿到里面的锁了
Lock锁必须配对,加了几把锁就要解几把锁!!
自旋锁
spinlock
自定义锁测试:
package com.liu.lock;
import java.util.concurrent.atomic.AtomicReference;
//自旋锁
public class SpinLockDemo01 {
//Thread null
AtomicReference<Thread> atomicReference = new AtomicReference<>();
//加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+ "==> mylock");
//自旋锁
while (!atomicReference.compareAndSet(null,thread)){//所期望的是空,更新为当前线程
}
}
//解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+ "==> myUnlock");
atomicReference.compareAndSet(thread,null);//如果是期望的线程,就置为空
}
}
package com.liu.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/*
测试结果:
t1线程先拿到锁,t2再拿到锁,自旋,等待t1解锁,t2才会解锁
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
//底层使用cas实现的自旋锁
SpinLockDemo01 lock = new SpinLockDemo01();
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t1").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lock.myLock();
try {
TimeUnit.SECONDS.sleep(2);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.myUnLock();
}
},"t2").start();
}
}
死锁
一句话理解:线程之间想获取其他线程的锁!
package com.liu.lock;
import java.util.concurrent.TimeUnit;
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
MyThread myThread = new MyThread(lockA,lockB);
MyThread myThread1 = new MyThread(lockB,lockA);
new Thread(myThread,"t1").start();
new Thread(myThread1,"t2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"lock:"+lockA+"==>"+lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"lock:"+lockB+"==>"+lockA);
}
}
}
}
解决死锁的问题:
1.使用jps -l
定位进程号,在终端里输入命令查看当前进程号
2.使用jstack 进程号
命令查看进程问题
读写锁
ReadWriteLock = new ReentrantReadWriteLock();
- 独占锁(写锁):一次只能被一个线程占用
- 共享锁(读锁):多个线程可以同时占有
Synchronized和Lock锁的区别
1.Synchronized是关键字,Lock是java类
2.Synchronized无法判断获取锁的状态,Lock可以判断是否获取锁
3.Synchronized会自动释放锁,Lock手动释放锁
4.Synchronized线程1获得锁,阻塞;线程2等待,Lock锁会trylock()抓取锁
5.Synchronized可重入锁,非公平锁,不可中断的,Lock锁可重入锁,可以中断,公平锁
6.Synchronized锁少量的代码块,Lock锁大量的代码块