和synchronized不同,lock在实现同步时需要手动的获取和释放对象锁,这样做虽然缺失了synchronized隐式获取锁的便捷,但是却带来了锁的可操作性,可中断性以及可以知晓线程是否成功获取锁。
Lock的特征详述如下:
1.尝试非阻塞地获取锁:当前线程尝试获取锁,如果这一时刻没有被其他线程获取到,则成功获取该锁。
2.能被中断的获取锁:与synchronized不用,lock获取的锁能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁将会被释放。
3.超时获取锁:通过构造参数来指定为获取锁而等待的时间,在该时间内没有成功获取锁则返回。
Lock类有几个重要的方法:
1.void lock():获取锁,调用该方法的当前线程将会获取锁,成功后返回。
2.void lockInterruptibly():可中断的获取锁,这中方式获取的锁能够响应中断。
3.boolean tryLock():尝试非阻塞式的去获取锁,调用该方法后成功获取则返回true。
4.boolean tryLock(long time,TimeUnit unit):超时获取锁,在等待时间内获取到锁、被中断或者等待时间结束就会返回。
5.void unlock():释放锁,一般放在finally代码块里。
6.Condition newCondition():获取一个Condition实例。
Lock的实现类是ReentrantLock,下面是使用Lock实现经典的售票同步问题:
package concurrency;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReenTrantLockTest implements Runnable{
private Lock lock = new ReentrantLock();
private int tickets =100;
private boolean flag = true;
@Override
public void run() {
while (flag) {
try {
// 加锁
lock.lock();
if (tickets > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "正在出售第" + (tickets--) + "张票");
flag = false;
}
} finally {
// 释放锁
lock.unlock();
flag = true;
}
}
}
}
public class SellTickets {
public static void main(String[] args){
ReenTrantLockTest reenTrantLockTest = new ReenTrantLockTest();
Thread t1 = new Thread(reenTrantLockTest,"窗口1");
Thread t2 = new Thread(reenTrantLockTest,"窗口2");
Thread t3 = new Thread(reenTrantLockTest,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
思路就是简单的利用获取锁和释放锁来保证同步,代码也比较简单,所以不作解释了,下一篇文章将会讲一下Lock实现等待/通知的接口Condition。