最近在重构证书管理的需求。之前的代码中,更新证书与吊销证书异步进行,没有进行同步控制。但是会导致主要的两个问题:
1. 更新证书线程和吊销证书线程会对相同的文件进行操作。
2. 不对这两个线程进行同步,日志内容交叉,不好查看。
所以我使用ReentrantLock结合Condition来保证更新证书线程与吊销证书线程交替执行。
ReentrantLock是synchronized的高级版本,而Object.wait()和Object.signal()又与condition.wait()和condition.signal()对应,所以我们先看一看synchronized,再进一步对比ReentrantLock。
synchronized锁定的是一块内存区域,而这把锁是一个对象。
synchronized有四种用法:
前面两种用法使用的锁是对象锁,而后两种锁使用的类锁。
即在需要执行被synchronize修饰的代码块(参数为this)和成员方法时,需要先获取当前的对象锁。如果两个线程同时都调用了同一个对象的一个被synchronize修饰的方法或方法里有synchronize修饰的代码时,这两个线程无法同时执行。
而在需要执行被synchronize修饰的代码块(参数为类对象)或者静态方法时,需要先获取当前的类对象。如果两个线程同时调用了同一个synchronize修饰的代码块(参数为类对象)或者静态方法时,这两个线程将无法同时执行。
执行Object.wait()会使当前线程进入阻塞状态,并且释放占用的锁。
执行Object.signal()会唤醒一个阻塞线程,使其进入就绪状态。此时被唤醒的线程会试图去获取锁。
实现线程交替执行的Java代码
public class Main { public static void main(String[] args) { Main main = new Main(); new Thread(() -> { for (int i = 0; i < 3; i++) { main.method(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 3; i++) { main.method(); } }, "B").start(); } private synchronized void method() { notify(); System.out.println(Thread.currentThread().getName()); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }
输出如下
A B A B A B
ReentrantLock提供了比synchronized更高级的功能,下面我们对比一下 使用上 这两种锁的异同点。
相同的部分:
无论是ReentrantLock还是synchronized都是可以重入的。可重入的是指可以申请同一个锁,可以处理这样一种情况:当线程需要递归执行时,重新进入方法还可以申请锁。
你可能会觉得递归进入同一个方法时,已经拥有了锁,为什么还要申请锁? 但是方法执行完之后会释放锁,那么如果我们不再重新申请锁,在内层递归执行完之后, 锁就释放了。回到外层方法时,此时没有锁保护该方法被排他性的访问。
都保证了可见性和互斥性。
其中可见性是指线程可以看到另一个线程的修改。那么我们会有一个疑问,保证了哪一部分变量的可见性呢?保证的是被两个线程使用到的共享变量,比如说两个线程都要使用到同一个对象的成员变量,那么一个线程的成员变量就会被两一个对象察觉。
为什么会存在可见性的问题? 原因是因为现在的CPU通常是多核,我们可以理解一个核会有一个线程跑在上面,而每一个核又有自己的缓存。这些缓存是 主内存的拷贝,线程在执行时,如果没有同步会直接修改自己缓存中的东西而不会修改主内存。
而互斥性则是指同一个内存区需被使用同一把锁的线程互斥地访问。
不同点:
Condition提供了Object的await()和notify()的功能。如下是Java doc中关于Condition的说明。我们可以得到这样的信息:Condition是为了使同一个对象产生多阻塞集的效果。使用synchronized方式同步,我们无法再锁定同一个区域的情况下,调用Object.wait来唤醒指定的线程。
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.