1.1 公平锁、非公平锁
公平锁:非常公平,不能够插队,必须先来后到。
非公平锁: 非常不公平,可以插队(默认是非公平锁)
代码示例
// 公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
1.2 可重入锁
1.2.1 可重入锁概念
同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。线程可以进入任何一个它已经拥有的锁,所同步着的代码块。ReentrantLock、Synchronized 就是一个典型的可重入锁,可重入锁最大的作用就是避免死锁。
1.2.2 可重入锁图解
注意: 拿到外面锁1后,里面的锁2, 锁3、可以自动获得。
1.2.3 代码示例
Synchronized版本
package cn.guardwhy.lock;
public class ReentrantLockDemo01 {
public static void main(String[] args) {
// 创建phone对象
Phone phone = new Phone();
new Thread(()->{
phone.sendMessage();
}, "curry").start();
new Thread(()->{
phone.sendMessage();
}, "kobe").start();
}
}
// 手机类
class Phone{
public synchronized void sendMessage(){
System.out.println(Thread.currentThread().getName() + "发短信...");
// 调用call方法
call();
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName() + "打电话...");
}
}
ReentrantLock版本
package cn.guardwhy.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo01 {
public static void main(String[] args) {
// 创建phone对象
Phone phone = new Phone();
new Thread(()->{
phone.sendMessage();
}, "curry").start();
new Thread(()->{
phone.sendMessage();
}, "kobe").start();
}
}
// 手机类
class Phone{
// 创建lock
Lock lock = new ReentrantLock();
// 发短信方法
public void sendMessage(){
lock.lock(); // 加锁操作,lock锁必须配对,不然容易出现死锁
try {
System.out.println(Thread.currentThread().getName() + "发短信...");
call(); // 调用call方法,这里也有一把锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
}
// 打电话方法
public synchronized void call(){
lock.lock(); // 加锁操作
try {
System.out.println(Thread.currentThread().getName() + "打电话...");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
执行结果
1.3 自旋锁
自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
1.3.1 源码分析
unsafe.getAndAddInt()
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
// 自旋锁
do {
// 获取传入对象的地址
var5 = this.getIntVolatile(var1, var2);
// 比较并交换,如果var1,var2 还是原来的 var5,就执行内存偏移+1; var5 +
var4
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
15.3.2 代码示例
package cn.guardwhy.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinlockDemo {
// 1.原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();
// 2.加锁操作
public void myLock(){
Thread thread = Thread.currentThread();// 获取线程
System.out.println(Thread.currentThread().getName() + "==>myLock");
// 3.自旋
while (!atomicReference.compareAndSet(null, thread)){
}
}
// 4. 解锁操作
public void myUnLock(){
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "==>myUnLock");
}
public static void main(String[] args) {
// 创建自旋锁对象,底层使用自旋锁CAS
SpinlockDemo spinlock = new SpinlockDemo();
new Thread(()->{
// 加锁
spinlock.myLock();
try {
// 休眠3s
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
spinlock.myUnLock();
}
}, "Curry").start();
try {
// 休眠1s
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
// 加锁
spinlock.myLock();
try {
// 休眠1s
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁
spinlock.myUnLock();
}
}, "Kobe").start();
}
}
1.3.3 执行结果
1.4 Java 死锁
1.4.1 死锁概念
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否者就会因为争夺有限的资源而陷入死锁。
1.4.2 产生死锁
1、系统资源不足
2、进程运行推进的顺序不合适
3、资源分配不当
1.4.3 代码示例
package cn.guardwhy.lock;
import java.util.concurrent.TimeUnit;
/*
* 死锁
*/
public class DeadLockDemo01 {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA, lockB), "Curry").start();
new Thread(new MyThread(lockB, lockA), "Kobe").start();
}
}
class MyThread implements Runnable{
// 定义lock锁
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 + "==>get "+ lockB);
try {
// 休眠2s
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName() + "lock:" + lockB + "==>get " + lockA);
}
}
}
}
执行结果
1.4.4 解决方案
1、查看JDK目录的bin目录。
2、使用 jps -l 命令定位进程号。
3、使用 jstack 进程号 找到死锁查看。
4、查看结果