美文网首页
Java高并发基础篇:并发与线程(一)

Java高并发基础篇:并发与线程(一)

作者: 浆水鱼鱼哈 | 来源:发表于2018-10-09 15:47 被阅读17次

    0x01、你必须知道线程是什么?

    线程是轻量级进程,是程序执行的最小单位

    在介绍线程之前,我们必须要知道进程的概念。进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是程序的基本执行体,线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    进程和线程是什么关系?

    进程是线程的容器,一个进程可以容纳若干个线程。

    我们可以把进程比喻成一个工厂的车间,线程就是车间里每个流水线上的工人,车间里的设备就是进程内的资源,工人在各自的流水线上使用设备生产,就像线程各自占用进程里的资源执行任务一样。

    0x02线程的生命周期或状态

    线程状态图

    线程的所有状态在Thread中的State枚举中定义:

    public enum State {
            NEW,
            RUNNABLE,
            BLOCKED,
            WAITING,
            TIMED_WAITING,
            TERMINATED;
        }
    
    • NEW:刚刚创建的线程,这种线程还未开始执行。
    • RUNNABLE:线程的start()方法调用时,表示线程开始执行,线程所需要的一切资源都准备好了。
    • BLOCKED:当线程在执行过程中遇到synchronized同步块,就会进入BLOCKED阻塞状态,线程暂停运行,直到获取资源的锁。
    • WAITING: 进入一个无时间限制的等待。
    • TIMED_WAITING:进入一个有时限的等待状态。
    • TERMINATED:线程执行完毕,表示结束。

    注意:从NEW状态触发后,线程不能再回到NEW状态,同理,处于TERMINATED的线程也不能再回到RUNNABLE状态。

    0x03并发的几个概念

    同步(Synchronous)和异步(Asynchronous)

    • 同步:方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续行为。
    • 异步:方法调用更像一个消息的传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作。


      同步和异步的方法调用

    临界区

    临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但一次只能由一个线程来使用它,其他线程想要使用,必须等待。
    在并行程序中,临界区资源是保护的对象。

    阻塞(Blocking)和非阻塞(Non-Blocking)

    阻塞和非阻塞通常用来形容多线程间的相互影响。当一个线程占用了临界区资源,其他需要这个资源的线程必须在临界区等待。等待会导致线程挂起,这种情况就是阻塞。
    非阻塞,强调没有一个线程可以妨碍其他线程的执行。

    死锁(Deadlock)、饥饿(Starvation)和活锁(Livelock)

    死锁、饥饿和活锁都属于多线程的活跃性问题,如果出现上述几种情况,那么相关线程可能就不再活跃,也就很难再继续执行下去。

    • 死锁:两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁

    产生死锁的四个必要条件:

    (1) 互斥条件:一个资源每次只能被一个进程使用。
    (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    (3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
    (4) 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。


    循环等待导致死锁产生
    • 饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行。如,线程优先级太低或者某一个线程一直占着关键资源不放,需要这个资源的线程无法正常执行。与死锁相比,饥饿是有可能在未来一段时间内解决的(比如高优先级的线程已经完成任务,不再执行)。
    • 活锁:当两个线程秉承“谦让”原则,主动将资源释放给对方使用,那么就会出现资源不断在两个线程中跳动,而没有一个线程可以拿到所有资源而正常执行。这种情况就是活锁。

    0x04并发级别

    由于临界区的存在,多线程之间的并发必须受到控制。根据并发的策略,可以把并发的级别进行分类,可分为阻塞、无饥饿、无障碍、无锁、无等待几种。

    阻塞(Blocking)

    一个线程是阻塞的,那么在其他线程释放资源之前(即当前线程未得到临界区的锁),当前线程无法继续执行(挂起等待)。使用synchronized关键字和重入锁得到的就是阻塞的线程。

    无饥饿(Starvation-Free)

    如果线程之间是有优先级的,那么线程调度的时候总是会倾向于满足高优先级的线程。也就是说,对于同一资源的分配是不公平的。这种非公平的锁来说,系统允许优先级高的线程插队。就可能会导致低优先级的线程产生饥饿。

    无障碍(Obstruction-Free)

    无障碍是一种最弱的非阻塞调度。无障碍执行不会因为临界区的问题导致一方被挂起,任何线程都可以进入临界区,修改共享数据。对于无障碍的线程来说,一旦检测到多方同时修改共享数据的情况,它会立即对自己所做的修改进行回滚,确保数据安全。

    无锁(Lock-Free)

    无锁的并行都是无障碍的,所有线程都能尝试对临界区进行访问。

    无等待(Wait-Free)

    无等待是一种在无锁的基础上更进一步进行扩展,它要求所有线程都必须在有限步内完成,这样就不会引起饥饿问题。

    0x05并发的原子性、可见性和有序性

    • 原子性(Atomicity):指一个操作是不可中断的。即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程干扰。
    • 可见性(Visibility):指当一个线程修改了某一个共享变量的值,其他线程是否能够立即知道这个修改。
    • 有序性(Ordering):即程序执行的顺序按照代码的先后顺序执行。但是一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排可以保证串行语义一致,但是没有义务保证多线程间的语义也一致。

    相关文章

      网友评论

          本文标题:Java高并发基础篇:并发与线程(一)

          本文链接:https://www.haomeiwen.com/subject/etkeaftx.html