你可能不了解的Synchonized和ReentrantLock

171 阅读4分钟
原文链接: mp.weixin.qq.com

面试中经常会问到的一类问题是多线程相关的,比如如何处理Android中的多线程通信,如何处理并发问题。归根结底,处理Java的并发,都离不开锁。我们将花三到四分钟了解两种锁的用法和不同,还有什么是公平锁。

ReentrantLock 和 Synchronized 的比较

相同:ReentrantLock提供了synchronized类似的功能和内存语义。不同:(1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;(2)ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。(3)ReentrantLock 的性能比synchronized会好点。(4)ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。

如何加锁
Synchronized

加锁有两种,方法同步(给方法增加synchronized声明)和代码块同步(给代码块增加synchronized声明)

ReentrantLock

获取锁的方式是通过 ReentrantLock 的对象 lock()来获取锁的,必须记住一定要在finally方法中调用 unlock() 释放锁

关于锁的重入

锁是可以重复获取的,Synchronized和 ReentrantLock都是

public class SynchronizedTest {
    public void method1() {
        synchronized (SynchronizedTest.class) {
            System.out.println("方法1获得ReentrantTest的锁运行了");
            method2();
        }
    }
    public void method2() {
        synchronized (SynchronizedTest.class) {
            System.out.println("方法1里面调用的方法2重入锁,也正常运行了");
        }
    }
    public static void main(String[] args) {                new SynchronizedTest().method1();
    }
}
public class ReentrantLockTest {
    private Lock lock = new ReentrantLock();
    public void method1() {
        lock.lock();                try {
            System.out.println("方法1获得ReentrantLock锁运行了");
            method2();
        } finally {
            lock.unlock();
        }
    }
    public void method2() {
        lock.lock();                try {
            System.out.println("方法1里面调用的方法2重入ReentrantLock锁,也正常运行了");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {                new ReentrantLockTest().method1();
    }
}
听说过公平锁吗?

Synchronized持有的锁是非公平的,因为CPU的调度算法是随机从等待队列里挑选一个线程,可能会导致优先级低的线程永远得不到锁。而ReentrantLock可以通过在构造方法中传入true来构造公平锁,保证线程按照时间的先后顺序执行。

public class LockFairTest implements Runnable{    //创建公平锁
    private static ReentrantLock lock=new ReentrantLock(true);
    public void run() {        while(true){
            lock.lock();            try{
                System.out.println(Thread.currentThread().getName()+"获得锁");
            }finally{
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        LockFairTest lft=new LockFairTest();
        Thread th1=new Thread(lft);
        Thread th2=new Thread(lft);
        th1.start();
        th2.start();
    }
}

结果如下

Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
Thread-0获得锁
Thread-1获得锁
总结

区别:1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁(tryLock()),而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。