Context:
关于Thread老是看了忘,看了忘记。还是写下来吧。翻译的是orcale官网的锁解释文档地址
锁:
同步机制是围绕内部实体即intrinsic lock(intrinsic固有的、本质的)、monitor lock(监控锁)。Intrinsic lock在同步上扮演两个角色:1.强制的排他性读取对象的状态(也就是线程访问对象的属性、方法等是互斥的)、2保持happens-before relationship来确保变量、方法的可见性。
每个对象都有与其关联的intrinsic lock。按照惯例,一个线程需要 排他的、一致性(consistent)读取对象的属性,必须获取该对象的锁。并在处理完后释放intrinsic lock。在线程获取和释放锁之间,线程被称为拥有intrinsic lock。只要一个线程持有intrinsic lock,其他的线程尝试获取这个锁时就会被阻塞。
两种Synchronized:
Synchronized方法:
用法: public synchronized void sayHello(){}
a)非静态方法:intrinsic lock是该调用方法的对象,如b.sayHello 线程持有的intrinsic lock就是对象b的intrinsic lock
b) 静态方法:例如 public static synchronized void getInstance()。调用是Test.getInstance();这里没有对象,线程持有的intrinsic lock是Test对象对应的Class类的对象,也就是假设Test类的对象testObject.getClass()所获得的对象。而该对象是在类的加载(Loading)阶段 在Java堆中生成一个代表改类的java.lang.Class对象,作为方法区的访问入口。(详见我以前的博客点击打开链接 )所以他是类级别的锁。
Synchronized声明:
用法:
public void addName(String name) {
synchronized(this) {
lastName = name;
nameCount++;
}
nameList.add(name);
}
a)不是用该方法的对象来做为intrinsic lock,而是你指定任意一个对象
好处
1:不对整个方法进行同步,而是对一部分进行同步,效率要高些,也更安全(从同步的方法中调用别的对象的方法,可能造成Liveness问(如:死锁)。如例子,就没有吧nameList.add(name);加到里面。
:
public class MsLunch {
private long c1 = 0;
private long c2 = 0;
private Object lock1 = new Object();
private Object lock2 = new Object();
public void inc1() {
synchronized(lock1) {
c1++;
}
}
public void inc2() {
synchronized(lock2) {
c2++;
}
}
}
2.c1的改变和c2的改变没什么逻辑上的关联,可以两个线程交叉改变。所以就没有必要用Synchronized Method,否则锁是该方法对象,两个inc1、inc2无法被两个线程同时访问。
重新进入Synchronized:
虽然a线程无法进入b线程锁住的方法,但是b线程可以再次进入b线程持有锁的方法,并对锁的进入进行累加。这样就可以递归调用了嘛。