synchronized关键字使用和理解

synchronized关键字使用和理解

      • 作用
      • 分类使用
      • 注意事项
      • 性质
      • 原理
      • 缺陷
    • 面试准备
      • 总结
      • 思考题

作用

保证多个线程同一时刻只能有一个线程执行同步块

分类使用

  1. 对象锁
    1. 修饰在普通方法的同步锁
      1. 使用方法
        public synchronized void method(){
            
        }
    
    1. 修饰在代码块的同步锁
        public void method(){
            synchronized(this){// 或者是其他的对象锁
                // dosomething
            }
        }
    
  2. 类锁(锁的对象是class对象)一个类可以有多个对象,但是只有一个class对象
    1. 修饰在static方法的同步块
        public static synchronized void staticMethod(){
            
        }
    
    1. 使用类名.class修饰的同步代码块
       public void staticMethod(){
           synchronized(类名.class){
               
           }
       }
    

注意事项

  1. synchronize同步块里面抛出异常后或者执行完毕才会释放锁
  2. 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待
  3. 每个对象实例都会有自己的一把锁,不同实例相互不影响

性质

  1. 可重入锁
    1. 概念:同一线程的外层函数获取锁之后,内层函数可以直接再次获取该锁(意思是:获取锁的方法里面调用了需要再次获取该锁的方法,这时候可以直接获取)
    2. 好处:避免死锁,提升封装性
    3. 可重入得粒度:线程范围内
  2. 不可中断
    1. 意思是:如果锁被其他线程获取到了,如果该线程想要获取锁只能等待获取锁的线程释放,如果获取锁的线程不释放锁,那么就只能永远等待下去,这就是不可中断性质。

原理

  1. 加锁释放锁的原理:现象、时机、查看字节码
    1. 现象:每一个Java实例对象都会有一把锁,每一个synchronize代码块都会指定一个锁对象,在执行同步代码块之前必须要获取该对象的锁方能执行,否则该线程会进入阻塞(blocking)状态,一旦线程获取到锁就会独占这把锁,不能被其他线程抢夺,直到同步代码块执行完毕或者执行中抛出异常jvm会自动释放该锁,后面阻塞的线程就会竞争这把锁,获取到进入同步代码块,否则继续阻塞。
    2. 时机:每次获取锁的时候会检查该锁的计数器是否为零,如果是获取该锁并给计数器加一,否则等待其他获取该锁的线程释放锁,如果一直没有释放就一直等待,因为synchronize不可中断。
    3. 查看字节码:进入到同步块执行monitorenter指令,退出时会执行monitorexit
  2. 可重入得原理:通过加锁的次数计数器,每次执行monitorenter并且加锁计数器加一,退出时计数器减一
  3. 保证可见性的原理:Java内存模型主要控制主内存和本地内存的间的通信来保证内存可见性,synchronize在释放锁的时候会同步本地内存到主内存中来保证内存可见性。
    synchronized关键字使用和理解_第1张图片

缺陷

  1. 效率低:锁的释放情况少(第一种,同步块执行完毕,第二种同步块抛出异常),试图获取锁不能设定超时(Lock锁可以设定超时),不能中断试图获取锁的线程(Lock可以中断)
  2. 不够灵活,加锁条件单一,只能使用对象头的锁,加锁和释放锁时机单一
  3. 无法知道是否成功获取到锁

面试准备

  1. 使用注意点:避免锁对象不能为空(锁的信息是实例对象的头部中的),作用域不宜过大(多线程的目的是提高运行效率,如果作用域过大,导致跟同步执行没有区别),避免产生死锁(注意加锁的顺序)
  2. 如何选择lock和synchronized关键字?
    1. 能避免使用锁就尽量不使用
    2. 如果能使用关键字synchronized解决问题尽量使用,原因可以减少编程带来的问题,如果需要使用到lock的特性,比如需要中断、需要设定超时时间,就是使用lock。
  3. 多线程访问同步方法的具体情况

总结

JVM会自动通过monitor来加锁和释放锁,并且保证同时只有一个线程能执行同步代码块,从而保证线程安全,同时具有了可重入性和不可中断性。

思考题

  1. 多个线程等待同一个synchronized锁的时候,JMV如何选择下个获取锁的线程?
  2. 什么是锁的升级、降级?什么事jvm里面的偏斜锁、轻量级锁、重量级锁?

你可能感兴趣的:(java多线程)