Lock的api的用法:
我们先看下lock接口中有哪些方法:
public interface Lock {
/**
* 获取锁,如果获取不到就一直等待,与synchronized一样
*/
void lock();
/**
* 该方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,
* 则这个线程能够响应中断,
* 即中断线程的等待状态。也就是说,
* 当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,
* 假若此时线程A获取到了锁,而线程B只有在等待,
* 那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
*/
void lockInterruptibly() throws InterruptedException;
/**
* 该方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,
* 如果获取失败(即锁已被其他线程获取),则返回false,
* 也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
*/
boolean tryLock();
/**
* 该方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,
* 在时间期限之内如果还拿不到锁,就返回false。
* 如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* 释放锁
*/
void unlock();
/**
*
* Condition是Java提供了来实现等待/通知的类,
* Condition类还提供比wait/notify更丰富的功能,
* Condition对象是由lock对象所创建的。但是同一个锁可以创建多个Condition的对象,
* 即创建多个对象监视器。这样的好处就是可以指定唤醒线程。
* notify唤醒的线程是随机唤醒一个 ,具体得会在后面线程间得通信讲 */
Condition newCondition();
}
ReentrantLock,意思是“可重入锁”,关于可重入锁,我们在前面synchronized中已经提到过,ReentrantLock简单得用法我们前面一篇也简单得用到过。ReentrantLock是实现了Lock接口的类,并且ReentrantLock提供了更多的方法,我们先看下ReentrantLock类继承关系:
public class ReentrantLock implements Lock, java.io.Serializable
我们可以看到实现了lock,和序列化接口。
那么就先使用下lock得api.
Lock()和unlock()我们上一章已经用到过,下面我们讲另外得方法:
tryLock()用法:
public class Account {
private Lock lock = new ReentrantLock();
public void add(BigDecimal amount , String name) {
//尝试获取锁,获取不到,直接跳出
if(lock.tryLock()) {
System.out.println(" 开始充值,线程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 结束充值,线程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}else {
System.out.println("获取锁失败,不进行充值,线程名: " + Thread.currentThread().getName());
}
}
}
然后继续充值线程类:
public class CzThread extends Thread{
private Account account;
private String accountName;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
account.add(new BigDecimal("100"), accountName);
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
主线程类:
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
CzThread cz1 = new CzThread(account);
cz1.setName("thread1");
cz1.start();
CzThread cz2 = new CzThread(account);
cz2.setName("thread2");
cz2.start();
}
}
执行结果:
获取锁失败,不进行充值,线程名: thread2
开始充值,线程名: thread1
结束充值,线程名: thread1
我们继续看下tryLock(long time, TimeUnit unit)用法,稍微修改下Account类:
public class Account {
private Lock lock = new ReentrantLock();
public void add(BigDecimal amount , String name) throws InterruptedException {
/***
* 等待了5s后
*/
if(lock.tryLock(5, TimeUnit.SECONDS)) {
System.out.println(" 开始充值,线程名: " + Thread.currentThread().getName());
try {
Thread.sleep(2000);
System.out.println(" 结束充值,线程名: " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}else {
System.out.println("获取锁失败,不进行充值,线程名: " + Thread.currentThread().getName());
}
}
}
充值类也要修改下,因为需要抛异常:
public class CzThread extends Thread{
private Account account;
private String accountName;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
try {
account.add(new BigDecimal("100"), accountName);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
}
看下执行结果:
开始充值,线程名: thread2
结束充值,线程名: thread2
开始充值,线程名: thread1
结束充值,线程名: thread1
我们看到线程1和2都执行完了,因为等待了5s得锁,所以会执行。
lockInterruptibly与Condition,我们会在后面得线程通信讲,下一篇我们会解析,我们上面讲的得方法得原理。