Java并发编程(基础)

本文详细介绍了并发编程的基础概念,如线程的异步并行特性、Java线程的使用方式,包括继承Thread和实现Runnable接口,以及线程的状态管理和中断机制。此外,还探讨了线程安全的关键概念,如Synchronized、Lock、Atomic、可见性和有序性,以及CAS(CompareandSwap)操作。文章还涉及JMM内存模型、Volatile关键字和避免伪共享的方法,最后以双检锁(DCL)为例讨论了单例模式在并发环境下的应用。
摘要由CSDN通过智能技术生成

并发编程(基础)

线程

线程是参与系统调度的最小单位,一个进程中可以并发执行多个线程。

线程的特征

  • 异步
  • 并行(CPU核数)

JAVA线程使用

  • 继承Thread
  • 实现Runnable、Callable
  • Future、CompleteFuture
  • 线程池

在这里插入图片描述

JAVA线程状态

在这里插入图片描述

如何停止线程

  • 线程自行运行结束
  • interrupt方法中断线程
    • 调用interrupt() 发送一个中断信号给某个线程。
    • 在线程的run方法中通过isInterrupted()判断是否被中断,自定义代码处理。
    • 如果run方法中有调用阻塞式的方法(比如sleep),当收到interrupt会抛出InterruptedException异常,在catch中自定代码处理中断逻辑。
  • 通过定义状态变量,判断变量的值中断线程,在外部调用方法修改某个变量的值,run方法中判断变量的值是否需要停止执行
class MyThread extends Thread{
        private volatile boolean isStop = false;
        @Override
        public void run() {
            int i = 0;
            while(!isStop){
                i++;
            }
        }
        
        public void setStop(boolean stop){
            this.isStop = stop;
        }
    }

线程安全

  • 原子性

    Synchronized, Lock, AtomicXXX(CAS)

  • 可见性

    Synchronized, Lock, Volatile

  • 有序性

    Synchronized, Lock, Volatile

synchronized

  • 修饰方法(实例方法、静态方法)
  • 修饰代码块

抢占锁的本质是什么

如何实现互斥?

  • 共享资源
  • 可以是一个标记,0无锁 1有锁

synchronized锁升级,线程安全和性能之间的平衡设计

  • 无锁
  • 偏向锁
  • 轻量级锁
  • 重量级锁

在这里插入图片描述

CAS

  • CAS, compare and swap的缩写,中文翻译成比较并交换。

  • CAS是原子性的操作:如果内存位置的值与预期原值相匹配,那么会将该位置的值更新为新值 。否则,不做任何操作。

/*
@param o 要修改的字段所属的对象
@param offset 字段在对象内的内存位置偏移量
@param expected 期望值(旧的值)
@param update 更新值(新的值)
@return true 更新成功 | false 更新失败
*/
public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt( Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong( Object o, long offset, long expected, long update);

线程安全之可见性

  • 两个线程有一个共享变量,一个线程修改了该变量的值后,另一个线程是否能实时读取到该变量的修改后的值

可见性的本质

  • CPU资源的利用问题
    • CPU增加高速缓存
    • 操作系统中,多线程通过CPU时间片切换,提升CPU利用率
    • 编译器(JVM的深度优化)

CPU高速缓存

  • 缓存行

    • 缓存行 (Cache Line) 是CPU Cache中的最小单位,CPU Cache由若干缓存行组成,一个缓存行的大小通常是 64 字节(这取决于 CPU)
  • 伪共享

    • 伪共享是指多个线程同时读写同一个缓存行中的不同变量时导致的CPU缓存失效。
    • 如何避免伪共享:对齐填充,java可以使用Contended注解

CPU缓存一致性问题

  • 总线锁
  • 缓存锁
    • 缓存一致性协议(MESI)
    • MESI表示缓存的四种状态:
      • Modify(修改)
      • Exclusive(独占)
      • Shared(共享)
      • Invalid(无效)

内存屏障

  • CPU层面不知道什么时候允许优化,什么时候不允许不优化,需要人为用指令告诉它

    • 读屏障
    • 写屏障
    • 全屏障
  • 防止指令重排

CPU优化之路

在这里插入图片描述

JMM(Java Memory Model)

在这里插入图片描述

Volatile

  • Volatile解决了可见性、有序性问题,用到CPU的缓存锁+内存屏障

final(内存屏障指令)

  • JMM内存模型禁止编译器把final域的写重排序到构造函数之外

Happens-Before

  • JVM会对代码进行编译优化,会出现指令重排序情况,为了避免编译优化对并发编程安全性的影响,需要happens-before规则定义一些禁止编译优化的场景,保证并发编程的正确性。
  1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生与书写在后面的操作。【保证单线程的有序】

  2. 锁定规则:一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。

  3. volatile 变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。【先写后读】

  4. 传递规则:A 先于 B 且 B 先于 C 则 A 先于 C

  5. 线程启动规则:Thread对象的start方法先行发生于此线程中的每一个动作。

  6. 线程中断规则:对线程 interrupt 方法的调用先行发生于被中断线程的代码检测到中断事件的发生。【先中断,后检测】

  7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过 Thread.join() 等待方法结束,Thread.isAlive() 的返回值手段检测线程已经终止执行。

  8. 对象终结规则:一个对象的初始化完成先行发生于它的 finalize 方法的开始。

DCL(Double Check Lock)

  • DCL是一种单例模式写法的简称,全称是Double Check Lock,翻译过来叫双重检查锁。

public class Singleton {
 
    //加上volatile,禁止new对象时的指令重排
    private static volatile Singleton singleton = null;
 
    private Singleton(){}
 
    public static Singleton getInstance(){
        //第1次检查时为了保证只有首次并发的情况下才阻塞,提高性能
        if (null == instance) {
            //加锁,是为了保证线程安全
            synchronized(Singleton.class) {
                //第2次检查时为了保证,避免重复创建对象
                if (null == instance) {
                    instance = new Singleton();
                }
            }
 
        }
        return singleton;
    }
}

  • new对象的过程并不是一个原子操作,有以下3步:

    分配内存 -> 初始化对象(构造方法) -> 变量的引用指向对象的内存地址

    该过程可能会指令重排序为:

    分配内存 -> 变量的引用指向对象的内存地址 -> 初始化对象(构造方法)

  • 如果不加volatile,多线程并发情况下可能会得到一个不完整的对象:

    线程1获取到同步锁进行对象的单例创建,当执行完分配内存、变量的引用指向对象的内存地址之后,还没来得及完成对象的初始化,此时线程2执行到判断变量不为null,于是返回了该变量,但拿到的是一个数据未初始化完整的对象。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值