文章目录
synchronized和lock的异同
synchronized 和 ReentrantLock 的区别
两者的共同点:
- 都是用来协调多线程对共享对象、变量的访问
- 都是可重入锁,同一线程可以多次获得同一个锁
- 都保证了可见性和互斥性
两者的不同点:
用法:ReentrantLock/Lock 是 API /接口级别的, synchronized 是 JVM 级别的,是 Java 中的关键字,内置的语言实现。
性能:
ReentrantLock 显示的获得、释放锁, synchronized 隐式获得释放锁;ReentrantLock 可响应中断、可轮回, synchronized等待的线程会一直等待下去, 是不可以响应中断的;
ReentrantLock 可以实现公平锁;
ReentrantLock 通过 Condition 可以绑定多个条件,进行线程的调度;
通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到;
Lock 可以提高多个线程进行读操作的效率,既就是实现读写锁等。
机制:
synchronized 在发生异常时,会自动释放线程占有的锁,以相反方向释放;因此不会导致死锁现象发生;
而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。
底层实现:
synchronized 是同步阻塞,使用的是悲观并发策略, lock 是同步非阻塞,采用的是乐观并发策略
运行独立
最好不要同时使用这两种同步机制,相当于两种不同类型的锁,使用时互不影响
当一个线程进入一个对象的一个synchronized方法后, 其它线程是否可进入此对象的其它方法?
分几种情况:(例子见java程序员面试笔试宝典154)
1.其他方法前是否加了synchronized关键字,如果没加,则能。
2.如果这个方法内部调用了wait,则可以进入其他synchronized方法。
3.如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
4.如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能 同步,因为非静态的方法用的是this。
重入锁(reentrantlock)公平与非公平
非公平
公平
释放
读写锁
1.写:排他锁。读:共享锁
之前提到锁(如Mutex和ReentrantLock)基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
除了保证写操作对读操作的可见性以及并发性的提升之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个共享的用作缓存数据结构,它大部分时间提供读服务例如查询和搜索),而写操作占有的时间很少,但是写操作完成之后的更新需要对后续的读服务可见。
2.读写状态的设计
3.锁降级
锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。
RentrantReadWriteLock不支持锁升级(把持读锁、获取写锁,最后释放读锁的过程)。目的也是保证数据可见性,如果读锁已被多个线程获取,其中任意线程成功获取了写锁并更新了数据,则其更新对其他获取到读锁的线程是不可见的。
condition类
await() signal() signalAll()
java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。
相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。
使用 Lock 来获取一个 Condition 对象。
public class AwaitSignalExample {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void before() {
lock.lock();
try {
System.out.println("before");
condition.signalAll();
} finally {lock.unlock();
}
}
ublic void after() {
lock.lock();
try {
condition.await();
System.out.println("after");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
ublic static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
AwaitSignalExample example = new AwaitSignalExample();
executorService.execute(() -> example.after());
executorService.execute(() -> example.before());
}
efore
after