并发编程基础知识

创建线程的三种方式

  1. 继承Thread对象,重写run方法
  2. 实现runnable接口,作为Thread构造参数 - Thread默认的run()方法中会调用runnable对象的run()方法
  3. 实现callable接口,配合FutureTask对象使用 - 底层依然是runnable接口,通过共享变量实现线程之间的控制和通信
/*
 *  实质:
 *    - Thread对象在start()方法中调用native方法通知操作系统创建线程,
 *    - 线程创建后,会回调Thread对象的run()方法实现业务调用
 */
public class ThreadTest {
    public static void main(String[] args) throws Exception{
        // 1. Thread类通过start()方法通知操作系统构建线程,操作系统创建线程后会回调Thread对象的run()方法
        Thread myThread = new MyThread();
        myThread.start();

        // 2. Thread类中存在一个runnable对象,默认的run()方法中会调用runnable对象的run()方法
        Runnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();

        // 3. 本质是对runnable接口的封装,添加future接口的实现,通过共享变量实现主线程与子线程之间的信息传递,包括:控制和返回值
        Callable callable = new MyCallable();
        FutureTask<String> task = new FutureTask<String>(callable);
        new Thread(task).start();
        
        String answer = task.get();
    }
}

操作系统中线程的生命周期

新建(New):新创建出来的线程,尚未执行start()方法
就绪(Ready):等待操作系统分配CPU时间片
运行(Running):正在运行中的线程
阻塞(Blocked):等待锁或者等待被唤醒
终结(Terminated):执行完成的线程
操作系统线程状态转换

JMM中线程的生命周期

新建(New):新创建出来的线程,尚未执行start()方法
运行态(Running):操作系统中就绪态和运行态的集合,代表正在执行中的线程
阻塞(Blocked):由Synchronized触发的阻塞状态
等待(Waiting): 等待被唤醒
限时等待(Timed Waiting):带超时时间的Waiting状态
终结(Terminated):执行完成的线程
java线程状态转换
tips: LockSupport是JMM为实现锁机制提供的类,其底层调用unsafe的park/unpark()方法

interrupt 线程中断标志

JMM底层为Thread提供了interrupt标志位,用于控制线程的执行。

  1. interrupt():将标志位设置为true(此时,会唤醒处于part()的线程)
  2. interrupted():将标志位恢复为false(使标志位复位,以等待下一次中断信号)
  3. isInterrupted():获取线程的中断状态

tips:interrupt标志位用于优雅地中止线程,当interrupt标志被置为true时,会唤醒对应的线程,由线程内部实现决定后续逻辑(InterruptedException)
tips:interrupt只发送中断信号,而是否中断以及具体的中断逻辑由线程自行决定

synchronized(类锁/对象锁)

  1. 特性:

    • 不可中断
    • 可重入(底层有重入计数器)
    • 非公平锁
  2. JDK 6对synchronized的优化:无锁化(偏向锁,轻量级锁)
    HotSpot class header

    • 偏向锁:适用场景 - 在同步周期内大概率不存在并发问题,通过CAS设置偏向标志,代表已有线程获取锁
    • 轻量级锁(自适应自旋锁):适用场景 - 大部分锁都会在很短时间内被释放,通过CAS自旋不断获取锁,避免线程挂起的损耗
    • 重量级锁:通过Monitor对象实现锁,内部实现为EntryList和WaitSet两个阻塞队列 - 用于同步状态(锁)和等待状态(wait - notify)

Synchronized - Moniter监视器锁的实现

死锁的条件

  • 互斥,共享资源 X 和 Y 只能被一个线程占用;
  • 占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
  • 不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
  • 循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。

volatile

并发编程问题:

  1. CPU存在多级缓存(会导致缓存一致性问题)
  2. 对指令实现重排序优化(编译器优化重排序/指令并行重排序/内存系统重排序),导致多线程任务下指令执行顺序的不可预测

volatile:

  • 即时可见性:修饰变量发生修改,会立刻被其他线程感知(可见) - 缓存一致性协议
  • 避免指令重排序:在修饰变量处插入内存屏障,避免指令重排序(实现指令的happen-before关系) - Lock字节码指令

缓存一致性协议(MESI):

  • M(Modify):缓存已被修改
  • E(Exclusive):独占缓存,数据只缓存在了当前CPU缓存中,未被修改
  • S(Shared):共享缓存,多个CPU均存储了该缓存,且未被修改
  • I(Invalid):缓存已失效

内存屏障:

  • 读内存屏障(处理失效的缓存): volatile在读操作之前会插入一个load屏障(刷新无效缓存,得到最新的值)
  • 写内存屏障(将当前缓存的值写回主存): volatile变量在写操作之后会插入一个store屏障(将之前store的缓存写入主存)
  • 读写内存屏障

Happens-Before模型

  • 程序次序规则(as-if-serial语义):单线程环境下,执行结果不变
  • volatile变量规则: volatile 修饰的变量的写操作,一定happens-before后续对于volatile变量的读操作.
  • 传递性规则:若a happens-before b,b happens-before c,则有 a happens-before c
  • start()规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
  • Join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。

Thread.join()

使当前线程阻塞,等待Thread结束后被唤醒,让线程的执行结果对后续线程可见(实现线程间的顺序性)
join代码流程图

ThreadLocal - 线程隔离存储机制

// ThreadLocal.class#get()
    public T get() {
        Thread t = Thread.currentThread();
        // 获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 从ThreadLocalMap中获取当前ThreadLocal对应的值
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

ThreadLocal底层结构

tips

  1. sleep(), join() 和 yiled() 的区别
  • sleep: 让线程睡眠指定时间,会释放cpu时间片(单不会释放锁资源)
  • join - wait()/notify(): 让主线程等待join线程的执行结果(happens-before模型join规则)
  • yiled: 让出时间片,触发重新调度,效果等同于sleep(0)
  1. i++操作是线程安全的吗?
    不是,i++底层字节码分为load,store两条指令,非原子性,可能存在并发问题

欢迎疑问、期待评论、感谢指点 -- kiqi,愿同您为友

-- 星河有灿灿,愿与之辉

原文地址:https://www.cnblogs.com/kiqi/p/14378975.html