用法:
比如我们使用信用卡消费,在消费中如果银行对卡片进行了冻结,那么扣款就应该会被拒绝。
此时就需要所有线程都能看到这个卡片状态的变化才行,否则就会造成用户损失。
要让这个状态被有线程看到,就需要使用 volatile
来修饰该变量。
扣款就更好理解了,如果不对账户的扣款动作进行加锁,账户相同的一笔钱可以被重复消费,将会造成银行的损失。加上锁之后,所有扣款行为将在这里串行进行,消费一笔扣减一笔,避免账户透支或者重复支付。
使用场景:
实现
Lock 是一个接口,而 synchronized
是 Java 中的关键字,由内置语言实现。
异常处理机制
synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁发生;
Lock 在发生异常时,如果没有主动通过 unlock() 方法去释放锁,则很可能造成死锁,因此使用 Lock 时需要在 finally 块中增加手动释放锁的语句。
synchronized{
语句块;
}
Lock lock = new ReentrantLock()
lock.lock();
lock.unLock();
lock() 和 unlock() 必须成对存在。
效率
Lock 可以提高多个线程进行读操作的效率(读写锁)。
ReadWriteLock
是读写锁:
public class MyCount {
private String id;//账号
private int cash;//账户余额
public MyCount(String id, int cash) {
this.id = id;
this.cash = cash;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
//读取操作
public int getCash() {
System.out.println(Thread.currentThread().getName() + " getcash, cash=" + cash);
return cash;
}
//写入操作
public void setCash(int cash) {
System.out.println(Thread.currentThread().getName() + " setcash, cash=" + cash);
this.cash = cash;
}
}
在用户信息类中声明了读写锁,
并在读取方法中创建读取锁,
在写入方法中创建写入锁,
所有锁在使用完后,均需要手动关闭锁。
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class User {
private String name;
private MyCount myCount;
//声明读写锁
private ReadWriteLock readWriteLock;
public User(String name, MyCount myCount) {
this.name = name;
this.myCount = myCount;
this.readWriteLock = new ReentrantReadWriteLock();
}
//查询余额
public void getCash(){
new Thread(){
@Override
public void run() {
//创建读取锁
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " getCash start");
myCount.getCash();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " getCash end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//手动关闭锁
readWriteLock.readLock().unlock();
}
}
}.start();
}
//设置余额
public void setCash(final int cash){
new Thread(){
@Override
public void run() {
//创建写入锁
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " setCash start");
myCount.setCash(cash);
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " setCash end");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//手动关闭锁
readWriteLock.writeLock().unlock();
}
}
}.start();
}
}
在测试类中创建用户及账户,并对账户信息使用多线程进行读取和写入操作。
public class Test {
public static void main(String[] args) {
//创建账户
MyCount myCount = new MyCount("abcd12", 5000);
//创建用户,并指定账户
User user = new User("小张", myCount);
for (int i = 0; i < 3; i++) {
user.getCash();
user.setCash((i + 1) * 1000);
}
}
}
Thread-0 getCash start Thread-0 getcash, cash=5000 Thread-2 getCash start Thread-2 getcash, cash=5000 Thread-2 getCash end Thread-0 getCash end Thread-1 setCash start Thread-1 setcash, cash=1000 Thread-1 setCash end Thread-4 getCash start Thread-4 getcash, cash=1000 Thread-4 getCash end Thread-5 setCash start Thread-5 setcash, cash=3000 Thread-5 setCash end Thread-3 setCash start Thread-3 setcash, cash=2000 Thread-3 setCash end
在输出的结果中,可以看到:
读取操作 getCash ,在 Thread-0 执行时,但未执行完,Thread-2 也同时进入到了读取操作,它们正在并行执行;
写入操作 setCash ,所有线程均从 start 到 end,中间并未有其他线程进入,属于独占执行。