synchronized是Java关键字, Lock是Java接口。
在Android开发中大多数情况下都使用synchronized,例如常用的StringBuffer、HashTable等线程安全的数据结构;但后台开发更多的用Lock。
1、 synchronized保证任何时刻最多只有一个线程执行被修饰的函数/代码块,不用显示申请锁、释放锁; 缺点是所有等待线程是线性逐个运行的。具体用法参考Java生产者消费者模式
2、 Lock更加灵活, 支持申请锁、释放锁等。
ReentrantLock类实现了Lock接口, 提供了申请锁、释放锁等基本功能;
ReentrantReadWriteLock类实现了ReadWriteLock接口, 提供了读写分离功能,即无写时支持多线程读,只能支持一个线程写。
StampedLock是升级版的读写锁, 写的时候也支持读, 但读时可判断当前是否在写并重读。
下面看Lock接口的定义, 包含申请锁、释放锁等功能。 在申请锁后必须要释放锁, 否则会出现死锁问题。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
API文档里介绍了Lock的用法, 先申请锁l.lock(), 得到锁后执行代码逻辑, 最后在finally里释放锁。
lock函数是无返回值的阻塞函数, 如果拿不到锁则阻塞等待, 直到获取到锁后继续向下执行。
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
}finally {
l.unlock();
}
tryLock是有返回值的非阻塞函数(能立即返回), 成功拿到锁后返回true, 失败时返回false。
Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
}finally {
lock.unlock();
}
} else {
// perform alternative actions
}}
tryLock(long time, TimeUnit unit)方法和tryLock()方法类似, 区别是如果拿不到锁会等待一段时间, 超时返回false。
void testLock() throws InterruptedException {
Lock lock = new ReentrantLock();
if (lock.tryLock(3, TimeUnit.SECONDS)) { //最多等待3秒
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alter