synchronized在操作过程中会访问操作系统,就是说java代码不能够让线程阻塞,过程依赖操作系统,所以是重量级锁
一个对象最少占16个字节,要做一个填充对齐,每个对象在内存中的大小都是8的倍数
对象头:java中的对象在内存中存储的时候有一个重要的组成部分,就是对象头,对象头中主要包括两部分数据:类型指针和标记字段,通过类型指针可以知道该对象是什么类型的,标记字段用来存储对象运行时的数据,其中就有锁对象的指针,它是我们理解Synchronized的关键,因为Synchronized使用到了各种锁。
标记字段中的锁对象指针指向了一个monitor对象,每个对象都有一个对应的monitor对象,monitor对象是同步工具,线程在执行加了Synchronized的代码段的时候要先去获取对象的monitor,执行完毕释放monitor。此过程是互斥的,一次只能有一个线程获取monitor,只有该线程释放monitor以后其它线程才能再获取它。
public class MonitorDemo implements Runnable {
int num = 0;
Object o = new Object();
@Override
public void run() {
synchronized (o) {
num++;
System.out.println(num);
}
}
}
线程一执行到“synchronized(o)”时获取到o对象的monitor进入同步代码块执行“num++; System.out.println(num);”,此时线程二执行“synchronized(o)”就无法获取o对象的monitor,只能等到线程一执行完同步代码块,将o的monitor释放才能获取到o的monitor,进入同步代码块执行。
当对某一段代码加上Synchronized关键字以后这段代码虽然拥有了线程安全性,但是效率也明显下降,如何在两者之间寻求平衡,在java1.6之后Synchronized进行了一些优化,引入了偏向锁、轻量级锁、重量级锁。
(大多数情况下,锁不仅不存在多线程竞争而且总是同一个线程获得这个锁,为了让获得锁的代价更低 CAS都不想做 偏向锁) 偏向锁撤锁会利用stop the world 由另一个线程来修改对象头会消耗cpu 尽量少使用
开机之后代码块运行这个对象,4s以后如果这个对象只有一个线程访问,会上一个偏向锁(实际上cas的wihle循环改成if)上面那一条线
如果出现了多线程访问就会出现轻量级锁(用cas来完成去取锁),轻量级锁再发现很多线程再抢这把锁,在3-5ms中还抢不到(吞吐量高)就会升级为重量级锁或者偏向锁调用了wait等也会变成重量级锁(重量级锁是把线程阻塞起来)
(轻量锁的CAS自旋:当其他线程发现无法执行该代码的时候(也就是拿不到锁,一直cas的拿锁)它并不会挂起而是开始自旋,也就是在那里等待,不断的尝试执行(CAS自旋),该操作会消耗cpu.自旋因为线程不需要被挂起和唤醒,执行速度很快,但是此时cpu在不停的运转,所以吞吐量会较低)
CAS自旋多了会出现自适应自旋锁 次数控制 时间:一个线程的上下文切换时间