zoukankan      html  css  js  c++  java
  • 线程同步 synchronized

    线程安全本质上是多个线程操作同一数据,要保证数据的准确。而Synchronized提供了线程互斥访问,同一时刻只能有一个线程来执行特定方法实现对数据的操作。

    使用方式:

    • 同步普通方法,锁的是当前对象。

    • 同步静态方法,锁的是当前 Class 对象。

    • 同步块,锁的是 {} 中的对象。

    可重入

    synchronized 是可重入锁。

    从设计上讲,当一个线程请求一个由其他线程持有的对象锁时,该线程会阻塞。当线程请求自己持有的对象锁时,如果该线程是重入锁,请求就会成功,否则阻塞。

    我们回来看synchronized,synchronized拥有强制原子性的内部锁机制,是一个可重入锁。因此,在一个线程使用synchronized方法时调用该对象另一个synchronized方法,即一个线程得到一个对象锁后再次请求该对象锁,是永远可以拿到锁的

    在 java 内部,同一线程在调用自己类中其他 synchronized 方法/块或调用父类的 synchronized 方法/块都不会阻碍该线程的执行。就是说同一线程对同一个对象锁是可重入的,而且同一个线程可以获取同一把锁多次,也就是可以多次重入

    实现原理

    每个锁关联一个线程持有者和一个计数器。当计数器为0时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。当一个线程请求成功后,JVM会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。当线程退出一个synchronized方法/块时,计数器会递减,如果计数器为0则释放该锁。

    线程的异常

    线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部,也就是说,线程方法的异常(无论是checked还是unchecked exception),都应该在线程代码边界之内(run方法内)进行try catch并处理掉

    异常会释放锁

     

    synchronized 既保证了原子性也保证了可见性。

    面试题:模拟银行账户,对业务写方法加锁,对业务读方法不加锁,这样行不行?(容易产生脏读。)

    因为这种情况不能保证写操作完成后再进行读操作,没写完就读容易读到不准确的中间态数据,即产生脏读。

    所以为了不产生这种情况,同时对读方法也进行加锁,保证读方法和写方法同时只有一个方法在执行。(给读方法也加synchronized)

     

    锁升级过程

    jdk1.5之前,是重量锁

    后来改进成锁升级。

    sync(Object)

    markword 记录这个线程id 偏向锁

    如果有线程正用,升级为 自旋锁 CAS

    自旋10次之后升级为重量级锁,向OS申请。

     

    什么时候用自旋锁(占用CPU)?什么时候用重量级锁(系统锁)?

    执行时间短(加锁代码),线程数少,用自旋锁。

    执行时间长,线程数多,用自旋锁。

    用户态/内核态

     

    总结一下:

    • 锁的是对象,不是代码;

    • 锁this XX.class

    • 锁定方法和非锁定方法可以同时执行

    • 锁升级:偏向锁-自旋锁-重量级锁

    • synchronized(Object) 不能用String常量,Integer,Long 基础数据类型

      对于Sting来说,其在堆内存中同一个值一般只有一份,也就是说两个String对象如果值相等,可能其对应的对象引用也完全一样,即两个对象完全一样

    •  

     

  • 相关阅读:
    单例模式
    leetcode:Minimum Subarray
    leetcode:Minimum Path Sum
    内存分配以及优化
    完整性检查
    类型定义之可选的一些笔记
    CSS|CSS module
    TS 操作符
    React|虚拟 Dom、render函数、shouldComponentUpdate
    React|常用相关框架
  • 原文地址:https://www.cnblogs.com/yunyunde/p/13903210.html
Copyright © 2011-2022 走看看