Synchronized简介与原理
Posted june0816
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Synchronized简介与原理相关的知识,希望对你有一定的参考价值。
一、作用
1. 多线程同步代码,保证方法或者代码块在运行时,同一时刻只有一个线程可以进入到临界区(互斥性)
2. 保证线程间共享变量的修改及时可见(可见性)
a. 当线程获取锁时,线程的本地变量无效,需要从主存中获取共享变量的值
b. 线程释放锁时,线程的本地变量被刷新到主存中
3. 有效解决重排序问题(有序性)
二、用法
1. Java中的每个对象都可以作为锁,获取的锁都是对象
2. 修饰函数,即普通同步方法,锁是当前类的实例对象 public void synchronized A(){}
3. 静态同步方法,锁是当前类的class对象 public static void synchronized A(){}
4. 修饰函数内的语句块,即同步代码块,锁是括号中的对象 synchronized(obj){}
5. 每个对象只有一个锁(lock)与之关联
6. 作用域
a. 某个对象实例内的方法,不同对象的实例内的方法不相干扰,其他线程可以同时访问相同类的其他对象实例中的synchronized方法
b. 某个类的范围,一般是静态方法,可以防止多个线程同时访问相同类中的synchronized方法
三、原理
1. java示例代码,同步静态方法
public class SynchronizedTest { private static Object object = new Object(); public static void main(String[] args) throws Exception{ synchronized(object) { } } public static synchronized void m() {} }
2. 代码->字节码->反编译后的代码
public static void main(java.lang.String[]) throws java.lang.Exception; descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic #2 // Field object:Ljava/lang/Object 3: dup 4: astore_1 5: monitorenter //监视器进入,获取锁 6: aload_1 7: monitorexit //监视器退出,释放锁 8: goto 16 11: astore_2 12: aload_1 13: monitorexit 14: aload_2 15: athrow 16: return public static synchronized void m(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=0, locals=0, args_size=0 0: return LineNumberTable: line 9: 0
3. 同步代码块使用monitorenter和monitorexit指令实现
4. 同步方法使用修饰符ACC_SYNCHRONIZED实现
5. 无论哪种实现,本质上都是对monitor的获取,这个过程是互斥的
四、锁的状态
1. 对象头:
a. 对象在内存中分为3部分:对象头、实例数据、对齐填充
b. 对象头可以记录对象的状态:偏向锁、轻量级锁、重量级锁
2. monitor:
a. 线程私有的数据结构,每个线程有一个monitor列表
b. JVM记录获取monitor锁的线程唯一id,确保一次只有一个线程执行
3. 偏向锁状态:
a. 如果一个线程获取了锁,那么锁就进入了偏向状态
b. 当这个线程再次请求锁时,不需要再做同步,就可以获取锁,避免了申请锁的操作,优化了性能
c. 适用于没有激烈竞争锁的情况,不仅没有多线程竞争,而且总是由同一个线程获取锁
4. 轻量级锁状态:
a. 获取偏向锁失败后,会膨胀为轻量级锁
b. 适用于交替执行同步块的情况
5. 重量级锁状态:
a. 获取轻量级锁失败后,会膨胀为重量级锁
b. 此时所有线程都会被锁住,当前获取锁的线程释放后,其他线程才被唤醒
五、等待通知机制,即wait(),notify(),notifyall()进行线程间通信
1. 代码
public class WaitNotify { static boolean flag = true; static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { Thread A = new Thread(new Wait(), "wait thread"); A.start(); TimeUnit.SECONDS.sleep(2); Thread B = new Thread(new Notify(), "notify thread"); B.start(); } static class Wait implements Runnable { @Override public void run() { synchronized (lock) { while (flag) { try { System.out.println(Thread.currentThread() + " flag is true"); lock.wait(); } catch (InterruptedException e) { } } System.out.println(Thread.currentThread() + " flag is false"); } } } static class Notify implements Runnable { @Override public void run() { synchronized (lock) { flag = false; lock.notifyAll(); try { TimeUnit.SECONDS.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
2. 使用wait(),notify(),notifyAll()时,需要先对对象加锁
3. 调用wait()方法后,线程会释放锁,线程状态RUNNING->WAITING,将线程移动到等待队列
4. notify()/notifyAll()时,等待线程不会立即从wait()返回,需要当前线程释放锁之后,才有机会获取锁返回
5. notify()将一个等待队列里的线程,移动到同步队列,线程状态WAITING->BLOCKED
6. notifyAll()将所有等待队列里的线程,移动到同步队列,线程状态WAITING->BLOCKED
7. 线程只有获取了锁,才能从wait()返回
参考:
https://www.cnblogs.com/mingyao123/p/7424911.html
https://www.jianshu.com/p/19f861ab749e
以上是关于Synchronized简介与原理的主要内容,如果未能解决你的问题,请参考以下文章
Java Synchronized 锁的实现原理与应用 (偏向锁,轻量锁,重量锁)
打通JAVA与内核系列之一ReentrantLock锁的实现原理
Java高并发编程实战4,synchronized与Lock底层原理