锁,在JAVA语言中,也就是synchronized关键字。
synchronized 是 Java 在语言层面提供的互斥原语,其实 Java 里面还有很多其他类型的锁,但作为互斥锁,原理都是相通的:锁,一定有一个要锁定的对象,至于这个锁定的对象要保护的资源以及在哪里加锁 / 解锁,就属于设计层面的事情了。
加锁本质就是在锁对象的对象头中写入当前线程id,但是new object每次在内存中都是新对象,所以加锁无效。
加锁锁住的必须是同一个对象,一个锁可以锁住1个或者多个资源,但是多个锁绝对不能同时锁一个资源。
当我们想要使用锁来锁住有关联关系的资源时,可以选择使用一个锁,粒度更大的锁,可以涵盖需要覆盖的资源,当然,必须要保证的是,锁的唯一性,即这个锁不会改变,不会因为资源的改变而改变。下面看一个:
保护有关联关系资源public class Account {
private int balance =1000;
//转账
void transfer(Account target,int amt) {
synchronized (Account.class) {//直接锁定这个类对象,就可以同时保护多个不不同对象的临界区
if (this.balance > amt) {
this.balance -= amt;
target.balance += amt;
}
}
}
public static void main(String[] args)throws InterruptedException {
Account account1 =new Account();
Account account2 =new Account();
Thread thread1 =new Thread(() -> {
account1.transfer(account2,500);
});
Thread thread2 =new Thread(() -> {
System.out.println(account1.getBalance());
});
Thread thread3 =new Thread(() -> {
System.out.println(account2.getBalance());
});
thread1.start();
thread1.join();
thread2.start();
thread2.join();
thread3.start();
thread3.join();
}
}
“原子性”的本质是什么?其实不是不可分割,不可分割只是外在表现,其本质是多个资源间有一致性的要求,操作的中间状态对外不可见。例如,在 32 位的机器上写 long 型变量有中间状态(只写了 64 位中的 32 位),在银行转账的操作中也有中间状态(账户 A减少了 100,账户 B 还没来得及发生变化)。所以解决原子性问题,是要保证中间状态对外不可见。
锁的使用,主要是对锁的理解,以及如何设置一个好的锁,如何设计一个好的锁,就必须结合实际应用场景来观察。资源之间是否有关系,等等,都需要考虑,而锁的覆盖范围,也都是要考虑的范畴。
网友评论