# 多线程编程 **Repository Path**: guozhuangzhuang1/Thread ## Basic Information - **Project Name**: 多线程编程 - **Description**: 内含面试常见的多线程手写题和多线程编程学习过程中的学习记录 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-02-16 - **Last Updated**: 2023-11-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: 多线程 ## README # 线程安全与优化 为了更深入理解线程安全,我们可以按照线程安全的“安全程度”来理解 ## 不可变 只要一个不可变的对象被正确的构建出来,那么永远都不会看到它在多个线程中处于不安全的状态 Java语言中,如果线程共享的是一个基本数据类型,那么用final修饰就好;如果为一个对象,那就用final修饰里面带有状态的变量 ## 绝对线程安全 Vertor不是绝对线程安全的,即使其内的方法都用syn修饰,也不意味着调用它的时候就永远不需同步手段了 ![1651755172819](src/images/img.png) ## 相对线程安全 就是我们通常来说的线程安全,需保证这个对象的单词操作是线程安全的,不需进行额外的保障措施,但是对于一些特定顺序的连续调用,就可能需要调用额外的同步措施来保证线程安全 大部分声称线程安全的类都属于此类 如:Vector,HashTable,Collections的synchronizedCollection()方法包装的集合等 ## 线程兼容 对象本身不是线程安全的,但是我们可以通过某些手段来保证调用时的线程安全 Java API中大部分类都是线程兼容的,如ArrayList,HashMap等等 ## 线程对立 不管调用端采用了什么措施都无法在多线程环境中并发使用代码,这种情况在Java中出现比较少 比如Thread类的suppend和resume方法,一个尝试中断一个尝试恢复,就有可能产生死锁 ## 实现线程安全的方法 ### 互斥同步(阻塞同步) 实现方式:临界区,互斥量,信号量 例如synchronize关键字 因为java的线程是映射到操作系统的原生内核线程之上的,所以涉及到用户态和内核态的切换 ![](src/images/2.png) 又比如Lock接口的实现类ReentrantLock,和synchronize不同的是:等待的线程可中断等待去执行其他的事情;可实现公平锁,根据线程申请锁的时间顺序来获取锁;锁可绑定多个条件,多个Condition对象 在synchronize没有进行优化之前,ReentrantLock确实性能比较好,但是在优化之后,性能已经不再成为衡量两者优缺点的要素。 ### 非阻塞同步 属于乐观的冲突检测策略 先进行操作,之后再进行共享数据的争用检测,采取补偿措施 常用的补偿措施就是不断重试,直到没有出现竞争为止,不需进行用户态和内核态的切换了 那么这种实现线程安全的措施是伴随着**硬件指令集**的发展而衍生出来的。 因为这个过程中**操作**和**冲突检测**这两个操作需具备原子性,通过处理器指令就可完成 比如常用的指令有CAS:有三个操作数分别是:1.变量的内存地址也就是当前变量的值-----》V;2.预期的旧值---------》A;准备设置的新值-----------》B。 通过判断V和A是否相等来检测是否出现了冲突,来决定是否更新。 ABA问题:解决方案为提供一个带有标记的原子引用类,通过控制变量值的版本来解决问题。但是这样的话还不如改为传统的阻塞同步方式。 ## 无同步方案 可重入代码,不依赖全局变量,堆上的数据,和公用的资源 #锁的升级过程 ![](src/images/3.png)