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线程持有锁的方法,并对锁的进入进行累加。这样就可以递归调用了嘛。