1、synchronized与lock的区别
1.1 原始组成
synchronized是属于JVM层面的关键字
Lock是具体类(java.util.concurrent.locks.Lock)是api层面的
1.2 使用方法
synchronized不需要用户去手动释放锁,当synchronized代码执行完毕后系统会自动释放锁
Lock(本文都用ReentrantLock举例),则需要用户去手动释放锁,若没有释放锁,就有可能导致死锁出现。通过lock()加锁、unlock()释放锁配合try / finally使用
1.3 等待是否可中断
synchronized不可中断,除非抛出异常或正常执行完毕
ReentrantLock可中断:
- 设置超时方法tryLock(long timeout, TimeUnit unit)
- lockInterruptibly()放代码块中,调用interrupt()方法可中断
1.4 加锁是否公平
synchronized非公平锁
ReentrantLock两者都可以,默认非公平,构造方法可以传入boolean值,true为公平,false为非公平
1.5 锁绑定多个条件Condition
synchronized不能
ReetrantLock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
案例说明:
/**
* 测试锁绑定多个条件Condition
* 案例:多线程之间按顺序调用,实现 A -> B -> C 三个线程启动,要求如下:
* A打印5次,B打印10次,C打印15次
* 这样循环10轮
*/
public class SyncAndReentrantLockDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
shareResource.printInfo(5);
}, "A").start();
new Thread(() -> {
shareResource.printInfo(10);
}, "B").start();
new Thread(() -> {
shareResource.printInfo(15);
}, "C").start();
}
}
}
/**
* 多线程调用资源类
*/
class ShareResource {
/**
* 定义A:1,B:2,C:3
*/
private int number = 1;
private Lock lock = new ReentrantLock();
private final Condition c1 = lock.newCondition();
private final Condition c2 = lock.newCondition();
private final Condition c3 = lock.newCondition();
private static Map<Integer, ConditionData> intToCondMap = new ConcurrentHashMap<>();
/**
* 这里只在主线程执行一次,相当于一个单线程可以不使用ConcurrentHashMap
*/
public ShareResource() {
intToCondMap.put(5, new ConditionData(1, c1, 2, c2));
intToCondMap.put(10, new ConditionData(2, c2, 3, c3));
intToCondMap.put(15, new ConditionData(3, c3, 1, c1));
}
public void printInfo(final int n) {
lock.lock();
try {
// 1、判断
ConditionData conditionData = intToCondMap.get(n);
while (number != conditionData.getCurNumber()) {
conditionData.getCurCond().await();
}
// 2、执行
printCount(n);
// 3、通知
number = conditionData.getNextNumber();
conditionData.getNextCond().signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void printCount(int n) {
for (int i = 1; i <= n; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
}
}
@AllArgsConstructor
@Getter
class ConditionData {
private int curNumber;
private Condition curCond;
private int nextNumber;
private Condition nextCond;
}