面试中经常会问到的一类问题是多线程相关的, 比如如何处理Android中的多线程通信,如何处理并发问题。归根结底,处理Java的并发,都离不开锁。 我们将花三到四分钟了解两种锁的用法和不同, 还有什么是公平锁。
相同:ReentrantLock提供了synchronized类似的功能和内存语义。 不同: (1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现; (2)ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。 (3)ReentrantLock 的性能比synchronized会好点。 (4)ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,要么一直阻塞,所以更容易产生死锁。
加锁有两种,方法同步(给方法增加synchronized声明)和代码块同步(给代码块增加synchronized声明)
获取锁的方式是通过 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可以提高多个线程进行读操作的效率。