synchronized: 锁定对象(在堆内存中),不能用String常量,Long,Integer 等基本类型;即保证了原子性又保证了可见性
原理
hotspot实现: hotspot是这样实现的,在一个对象的头64位,拿出2位来记录这个对象是否被锁定,并存入线程ID(mark word)
hotsport实现步骤
1.第一次 markword 记录这个线程ID,没有其他线程访问 (偏向锁)
2.线程争用 (自旋锁)-用户空间,会占用CPU
其他线程争用,首先是自旋(理解为while循环心跳检测,超过10次升级为重量级锁)
3.升级为 (重量级锁)- 内核空间,不会占用CPU
重量级锁会去操作系统申请锁资源,经过OS,进入等待队列,不再占用CPU
注意:
1.锁一旦升级了,就不能降级
2.执行时间长的,线程数多,用重量级锁(系统锁)
3.执行时间短的(加锁代码),线程数少,用自选锁
实例一、sycnchronized锁定某个对象
//hotspot是这样实现的,在一个对象的头64位,拿出2位来记录这个对象是否被锁定(mark word)
private int count = 10;
Object o = new Object();
public static void main(String[] args) {
}
private void syncTest(){
synchronized (o){//任何线程要执行以下代码,都必须先拿到锁
count --;
}
}
实例二、synchronized锁定this
private synchronized void syncTest(){
count --;
}
等于
private void syncTest(){
sychronized(this){
count --;
}
}
实例三、synchronized Class对象
private static synchronized void syncTest(){ //等价synchronized(Test.class)
count --;
}
实例四:引用
Class T1{
private synchronized void m1(){
system.out.println("m1 start");
Thread.sleep(5000);
System.out.println("m1 end");
}
private void m2(){
system.out.println("m2 invoke");
}
public static void main(String[] args){
new Tread(t.m1);
new Thread(t.m2);
}
//print
//m1 start
//m2 invoke
//m1 end
}
实例五: 银行账号,写加锁,读不加锁,产生脏读
todo
实例六、锁可以重入
重入是针对同一个线程
private synchronized void m1(){
m2();
};
private synchronized void m2(){
}
实例七、父子类锁重入
public class Parent{
private synchronized void m(){
System.out.println("this is parent");
}
}
public class Child extends Parent{
private synchronized void m(){
System.out.println("this is child start");
super.m();
System.out.println("this is child end");
}
}
实例八、异常会释放锁
private synchronized void m(){
for(int i =0;i < 100; i ++){
1/0; //异常释放锁
...
}
}
实例八:锁的细化
public void m(){
count++;
synchronized(this){
...
}
...
}