synchronized的使用方式有两种
1 对一个对象进行加锁
synchronized(O){ //代码 }
2 对一个方法加锁
public synchornized void func(){ //代码 }
其实无论是对一个对象进行加锁还是对一个方法进行加锁,实际上,都是对对象进行加锁。
java中一个对象由三部分组成=>
1 对象头=>markwork(hashCode(只有在调用后才会有),GC分代年龄,锁信息),class指针,如果是数组的话还会有一个数组的长度
2 实例数据=》存放对象字段的数据
3 填充数据=》为了保证一个对象所占用的字节数是8的倍数,提高执行效率
4 如果对象是数组的话对象头
偏向锁的概念一直不太好理解,偏向锁其实没有上锁,只是将线程指针加到对象头中,在更改的时候原理是cas。之所要偏向锁,是因为大部分情况下都是一个进程在操作,当有竞争的时候,需要将锁升级(一般升级为轻量级锁,如果加了await就直接进入重量级锁,在升为轻量级锁的时候,偏向的线程也要和其他的线程重新进行cas操作来获取锁),但是在升级的时候会让偏向的这个线程先获取锁,偏向锁的意义就是为了让偏向的线程先获取锁。
偏向锁产生的条件?=》只有偏向锁已启动(即偏向锁的标志位为1)的对象才能产生偏向锁,如果没有则直接进入轻量级锁。
对象如何启动偏向?=》只有在jvm启动一段时间(一般4秒钟)后创建的对象jvm才会给该对象头的偏向锁标志位设置为1。
代码示例:
public static void main(String[] args) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { Object o = new Object(); synchronized (o){ System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }).start(); }
偏向锁是否一定比自旋锁效率高?
不一定,在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销,这时候直接使用自旋锁。
可见性:
JMM关于synchronized的两条规定:
1)在线程执行的代码中遇到释放锁前,必须把工作内存中共享变量的最新值刷新到主内存中
2)在线程执行的代码中中遇到获得锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值
public class Test { private static int x = 0; private static int y = 0; public static void main(String[] args) { new Thread(() -> { while (true) { synchronized (Test.class) { } if (x == 1 & y == 2) { System.out.println("thread1 end"); break; } } }).start(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } x = 1; y = 2; }).start(); System.out.println("main is end"); } }