JAVA - 锁

126 阅读2分钟

Synchorized 和 ReentrantLock 比较

一、实现的区别

Synchorized是java关键字,是jvm层面的锁,有锁升级的概念

代码块同步:

public void sync(){
    synchronized (this){

    }
}

同步方法

public synchronized void sync(){
    
}

ReentrantLock 由JUC(java.util.concurrent)包提供的API层面,由java代码实现的锁,底层通过CAS保证线程的原子性操作,以及核心的state (volatile保证数据可见性)值的比较来实现锁的功能

new ReentrantLock();

二、解锁方式不同

Synchorized 由系统自动释放线程所持有的锁,如果同步代码块或者同步方法中的任务一直无法执行完毕可能导致死锁

死锁 (举例)

public static void sync01(){

    synchronized ("lock"){
        while (true){
            System.out.println(Thread.currentThread() + "持有锁");
        }
    }
}

public static void sync02(){

    synchronized ("lock"){
        while (true){
            System.out.println(Thread.currentThread() + "持有锁");
        }
    }
}

ReentrantLock 手动lock()上锁,在finally 中手动unlock()释放锁,如果不手动释放锁一样可能导致死锁,不过相比于synchorized更安全,可以代码控制的;并且提供了lock.tryLock(timeout, unit),如果在设定的时间内未获取到锁则放弃获取执行后续代码

public static void reentrantLockTestA(){
    lock.lock();
    try {
        TimeUnit.SECONDS.sleep(4);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }finally {
        lock.unlock();
    }
}

public static void reentrantLockTestB(){
    try {
        boolean succ = lock.tryLock(3, TimeUnit.SECONDS);
        if (succ) {
            System.out.println(Thread.currentThread() + "获取到了锁");
        }else {
            System.out.println(Thread.currentThread() + "超过了等待时间,放弃获取锁");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    new Thread(() -> reentrantLockTestA(),"线程A").start();

    new Thread(() -> reentrantLockTestB(),"线程B").start();
    
}

结果:
Thread[线程B,5,main]超过了等待时间,放弃获取锁

三、公平锁/非公平锁

简介: 多线程共同争夺一把锁,谁等待时间长谁先获取则为公平锁,类型先进先出,反之则为非公平锁

Synchorized 非公平锁

ReentrantLock new ReentrantLock() 可以传参的,默认为false非公平锁,传true则为公平锁

源码:

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

四、其他特性

ReentrantLock 可以和Condition配合使用,Condition接口提供了await()/signal()/signalAll()方法,可以精确唤醒指定线程或是全部等待线程;而Synchorized 只能通过Object类 await()/notify()/notifyAll(),随机唤醒某一个线程或是全部线程

Synchorized 只有遇到异常或者正常执行完才会中断

ReentrantLock lockInterruptibly() 可以中断线程

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}

中断测试:

public static void test9() throws InterruptedException {
    System.out.println(Thread.currentThread() + "start");

    try {
        System.out.println("balabalabala");
        lock.lockInterruptibly();
    }catch (Exception e){
        System.out.println(Thread.currentThread() + "被中断");
    }

}


public static void main(String[] args) {

    Thread t1 = new Thread(() -> {
        try {
            test9();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "线程A");
    t1.start();
    t1.interrupt();
}

结果:
Thread[线程A,5,main]start
balabalabala
Thread[线程A,5,main]被中断