天天看点

JUC - Java锁

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、可以自动获得。

JUC - Java锁

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();
        }
    }
}
           

执行结果

JUC - Java锁

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 执行结果

JUC - Java锁

1.4 Java 死锁

1.4.1 死锁概念

JUC - Java锁

死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否者就会因为争夺有限的资源而陷入死锁。

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);
            }
        }
    }
}
           

执行结果

JUC - Java锁

1.4.4 解决方案

1、查看JDK目录的bin目录。

2、使用 jps -l 命令定位进程号。

JUC - Java锁

3、使用 jstack 进程号 找到死锁查看。

JUC - Java锁

4、查看结果

JUC - Java锁

继续阅读