Java中的Lock与synchronized的区别

Java中的Lock与synchronized的区别

先弄清一个问题:

一、什么是可重入锁?

可重入锁就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。

看一个synchronized的例子:

package com.pipi.juc;

public class WhatReentrant {
    public static void main(String[] args) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this) {
                    System.out.println("第1次获取锁,这个锁是:" + this);
                    int index = 1;
                    while (true) {
                        synchronized (this) {
                            System.out.println("第" + (++index) + "次获取锁,这个锁是:" + this);
                        }
                        if (index == 8) {
                            break;  // 跳出死循环
                        }
                    }
                }
            }
        }, "t1").start();

    }
}

运行结果:

1次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b142次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b143次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b144次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b145次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b146次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b147次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b148次获取锁,这个锁是:com.pipi.juc.WhatReentrant$1@54005b14

Process finished with exit code 0

我们发现程序正常运行,说明 synchronized 就是可重入锁


我们再看个例子:

package com.pipi.juc;

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class WhatReentrant {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();  // 声明锁对象

        new Thread(() -> {
        	lock.lock();  // 第一次锁
            try {
                System.out.println("第1次获取锁,这个锁是:" + lock);

                int index = 1;
                while (true) {
                    lock.lock();  // 循环内多次获得锁
                    try {
                        System.out.println("第" + (++index) + "次获取锁,这个锁是:" + lock);
                        try { TimeUnit.MILLISECONDS.sleep(new Random().nextInt(200)); } catch (InterruptedException e) { e.printStackTrace(); }
                        if (index == 8) break;  // 跳出循环
                    } finally {
                        lock.unlock();  // 每循环1次,解锁1次
                    }
                }
            } finally {
                lock.unlock();  // 对应第1次上锁,释放掉
            }
        }, "t1").start();

    }
}
1次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]2次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]3次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]4次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]5次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]6次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]7次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]8次获取锁,这个锁是:java.util.concurrent.locks.ReentrantLock@5103fb35[Locked by thread t1]

Process finished with exit code 0

我们发现程序也正常运行,说明 ReentrantLock 也是可重入锁

我们得出第一个结论:
Lock是一个接口,synchronized是java中的一个关键字。
Lock 和 synchronized都是可重入锁。


2、Lock要我们手动写代码释放锁,synchronized是线程执行完自动释放锁。

我们让Lock上锁2次,释放锁1次,让另一个线程再去调用:

package com.pipi.juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class WhatReentrant {
    public static void main(String[] args) {

        final Lock lock = new ReentrantLock();

        new Thread(() -> {
            lock.lock();
            lock.lock();  // 锁了2次
            try {
                try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.println("lock()两次,unlock()一次");
            } finally {
                lock.unlock();  // 这里释放1次锁
            }
        }, "t1").start();

        new Thread(() -> {
            lock.lock();  // 另外的线程,获取锁
            try {
                System.out.println("我这句话能输出吗?");
            } finally {
                lock.unlock();
            }
        }, "t2").start();

    }
}

运行结果,我们发现第二个线程的打印一直处于阻塞等待状态。
我们unlock()两次,再次运行,发现程序正常输出。

所以,我们得出第二个结论:
Lock需要程序员手动释放锁,不然可能出现死锁情况。


3、我们再看下Lock接口的源码:

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

方法解释:

  • lock()是我们平时用的最多的,最用是用来获取锁,如果锁已经被其他线程获取,那么就等待。
  • tryLock()是一个boolean类型的方法,当调用这个方法的时候,线程会去尝试获取锁,如果获取到的话会返回true,如果获取不到返回false,也就是说这个方法会立马返回一个结果,线程不会等待。
  • tryLock(long time, TimeUnit unit)是上面tryLock()方法的一个重载方法,加了两个参数,给定了等待的时间,如果在规定时间拿到锁返回true,如果拿不到返回false。
  • lockInterruptibly()就比较特殊了,它表示可被中断的,意思就是,当尝试获取锁的时候,如果获取不到的话就会等待,但是,在等待的过程中它是可以响应中断的,也就是中断线程的等待过程。
  • unlock(),释放锁。
  • newCondition(),返回Condition对象,用于线程之间的通信,Condition的await(),signalAll()方法,相当于Object的wait(),notifyAll()方法。

结论:

synchronized是Java中的一个关键字,而Lock是Java1.5后在java.util.concurrent.locks包下提供的一种实现同步的方法,那么显然的,synchronized一定是有什么做不到的或者缺陷,才导致了Lock的诞生。

synchronized是关键字,Lock是接口,二者均可实现线程同步;

Lock 和 synchronized都是可重入锁

synchronized执行完自动释放锁,Lock需要程序员手动释放锁,不然可能出现死锁情况;

使用synchronized线程不释放锁,其他线程会一直等待下去,直到使用完释放或者出现异常,而Lock可以使用可以响应中断的锁或者使用规定等待时间的锁;

synchronized无法得知是否获取到锁,而Lcok可以做到。

所以综上所述:Lock的性能会远高于synchronized。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值