Lock与Condition
Lock
与Condition
是Java在1.5
中引入的,
Lock
用来解决同步的问题, Condition
用于解决协同的问题,
Lock
对应synchronized
, Condition
对应wait() / nofity()
,
Lock/Condition与synchronized/wait/notify机制的差别
synchronized可以做不到, lock可以做到的事情
-
synchronized
锁获取锁的条件只有一个, 那就是对同一个对象资源进行锁竞争,无法拓展一些条件来判定是否可以获取锁。 -
synchronized
没有办法实现读写分离, 当有多个线程进行一个资源的获取时,可能只有一个线程是写,其他的线程只是想读, 但是synchronized
不关心是读还是写,只要有多个线程来访问同一个资源,必须串行化等待。 -
synchronized
无法做到判断一个锁是否被占有,例如一个线程尝试获取一个资源时,如果这个资源被占有,这个线程就去做别的事情,如果没有被占有,则获取资源,synchronized
当一个线程尝试获取被占有资源时,只能将其置为等待。 -
synchronized
无法实现公平锁,抢占锁时,很有可能后来的线程比先来的线程先行拿到锁,这样可能会造成一些线程一直在等待, 俗称线程饥饿
。 -
synchronized
无法在一个锁方法内通过条件判断释放当前的锁,只能等待当前方法完全执行结束。
lock无法做到的事情
synchronized
可以在方法执行完毕或抛出异常后自动释放锁,而Lock则需要自己手动解锁,尤其是解锁操作为了安全一定要放在finally
中, 注意Lock加锁和解锁必须成对出现,否则将造成锁无法释放, 其他的线程无法获取到锁
。
public synchronized static void syncHello() {
System.out.println("hello");
}
public static void lockHello() {
lock.lock();
try {
System.out.println("hello");
} finally {
lock.unlock();
}
}
两者都可以做到的事情
两者都可以做到可重入锁。
synchronized
的设定之一就是可重入锁,所谓可重入锁的意思就是一个线程持有了一个锁之后,在执行的过程当中,
又碰到了这个锁,那这时候就可以继续进入这个锁,不会因为这个锁资源已经被自己持有着,就不能再次进入,这个符合常规的思维逻辑,因为这个东西本身就在咱自己手上,咱当然可以一直使用。
synchronized
实现的重入锁:
public static void main(String[] args) {
a();
b();
}
public synchronized static void a() {
System.out.println("a");
}
public synchronized static void b() {
System.out.println("b");
}
Lock
实现的可重入锁,注意此处使用的是Lock
的实现类ReentrantLock
, 因为Lock
自身是一个接口,有很多的实现类,
而ReentrantLock
是Lock
下的子类之一,翻译成中文就是可重入的锁
, 日常使用过程中,可重入锁是最常用到的,所以该类是使用最频繁的类。
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
a();
b();
}
public static void a() {
lock.lock();
System.out.println("a");
lock.unlock();
}
public static void b() {
lock.lock();
System.out.println("b");
lock.unlock();
}
Condition
lock
用于线程的上锁与解锁,而Condition
则是用于线程的协同,例如何时进行等待,何时进行唤醒,常用的方法有:
await
,当前线程进入等待状态,并将锁让给其他人signal
,通知一个await
的线程可以继续执行signalAll
,通知所有在当前lock
锁中await
的线程可以进行执行,但被唤醒的多个线程仍然要再次尝试获取到锁后才可以执行(意思是唤醒了多个线程后,为了保证线程安全,只有一个线程能够h获取到锁继续执行下面的代码)
用这个类主要是可以决定线程什么时候该停下来,什么时候开始做事,下面我们利用lock
和condition
来实现一个生产者 + 消费者
模型。