java 多线程基础(一)

同步与异步

    同步和异步的概念对于不少人来讲是一个模糊的概念。其实咱们的生活中存在着不少同步异步的例子。好比:你叫我去吃饭,我听到了就马上和你去吃饭,若是咱们有听到,你就会一直叫我,直到我听见和你一块儿去吃饭,这个过程叫同步;异步过程指你叫我去吃饭,而后你就去吃饭了,而无论我是否和你一块儿去吃饭。安全

  • 同步交互:指发送一个请求,须要等待返回,而后才可以发送下一个请求,有个等待过程;多线程

  • 异步交互:指发送一个请求,不须要等待返回,随时能够再发送下一个请求,即不须要等待。并发

线程与进程

    进程就是一个应用程序在处理机上的一次执行过程,它是一个动态的概念,而线程是进程中的一部分,进程包含多个线程在运行。以下是网上看到的一个很好的例子异步

    线程与进程我的的理解比如地铁5号线中的一条线路的阻塞会影响整段5号线的运行。间接的也会影响到其余地铁线路的阻塞。进程比如站点(国贸站,大望路站等),线程比如线路一个站点能够有多条线路,站点不是活动的实体是没有能力调配整套地铁资源的。只有中央系统才能统一操做活动的实体。spa

    进程做为地点基本单位,线程做为独立运行和路线调度的基本单位,不须要拥有资源(如厕所,空调,美化等),因此付出的开销就比较小,多个线路的的通行能更高效提升系统。每条线路有本身设计空间和目标站点就也是所说执行上下文。子进程和父进程有不一样的车站(5号线和1号线东单站),而多个线路可共享同一个站点。线程

并行与并发

  • 并发:实质是一个物理CPU(也能够多个物理CPU) 在若干道程序之间多路复用,并发性是对有限物理资源强制行使多用户共享以提升效率。设计

  • 并行:性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不一样CPU上同时执行。

并发,是在同一个cpu上同时(不是真正的同时,而是看来是同时,由于cpu要在多个程序间切换)运行多个程序。code

 

并发

并行,是每一个cpu运行一个程序。进程

并发

打个比方。并发,就像一我的(cpu)喂2个孩子(程序),轮换着每人喂一口,表面上两个孩子都在吃饭。并行,就是2我的喂2个孩子,两个孩子也同时在吃饭。事件

临界区

    临界区(Critical Section)是一段供线程独占式访问的代码,也就是说如有一线程正在访问该代码段,其它线程想要访问,只能等待当前线程离开该代码段方可进入,这样保证了线程安全。他工做于用户级(相对于内核级),在Window系统中CRITICAL_SECTION实现临界区相关机制。

阻塞与非阻塞

    阻塞与非阻塞一般来形容多线程间的相互影响。好比一个线程占用了临界区资源,那么其余全部须要这个资源的线程就必须在这个临界区中进行等待。等待会致使线程挂起,这种状况就是阻塞,此时,若是占用资源的线程一直不肯意释放资源,那么其余全部阻塞在这个临界区的线程都不能正常工做。

    非阻塞与值相反,他强调没有一i个县城能够妨碍其余线程执行,全部的线程都会尝试不断向前执行,接下来会详细描述。

死锁、饥饿和活锁

    死锁、饥饿和活锁都属于多线程的活跃性问题。若是发生以上几种状况,那么线程可能很难在继续往下执行了。

死锁

    死锁是最糟糕的一种状况,指两个或两个以上的进程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。因为资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而没法继续运行,这就产生了一种特殊现象死锁。

    虽然进程在运行过程当中,可能发生死锁,但死锁的发生也必须具有必定的条件,死锁的发生必须具有如下四个必要条件。

  • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。若是此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
  • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对本身已得到的其它资源保持不放。
  • 不剥夺条件:指进程已得到的资源,在未使用完以前,不能被剥夺,只能在使用完时由本身释放。
  • 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

饥饿

    饥饿是指某一个或多个线程由于种种缘由没法得到所须要的资源,致使一直没法执行。好比它的线程优先级可能过低,而高优先级线程不断抢占它所需的资源,致使低优先级线程没法工做。还有一种状况就是某一线程一直站着关键资源不放,致使其余须要这个资源的县城没法正常执行,这种状况也是饥饿的一种。与死锁相比,饥饿仍是有可能在将来的一段事件内解决的。

活锁

    活锁指事物1可使用资源,但它让其余事物先使用资源;事物2可使用资源,但它也让其余事物先使用资源,因而二者一直谦让,都没法使用资源。 

并发级别

    因为临界区的存在,多线程之间的并发必须受到控制。根据控制并发的策略,咱们能够把并发的级别进行分类,大体上能够分为阻塞、无饥饿、无障碍、死锁、无等待几种。

阻塞

一个县城是阻塞的,那么在其余线程释放以前,当前线程没法继续工做。当咱们使用synchronized关键字,或者重入锁时咱们获得的就是阻塞的线程。

    不管是synchronized或者重入锁,都会试图在执行后续代码前,获得临界区的锁,若是得不到,线程就会被挂起等待,直到占用了所需资源为止。

无饥饿

    若是线程以前是有优先级的,那么线程调度的时候老是会倾向于高优先级线程。也就是说同一资源的分配是不公平的。对于非公平的锁来讲,系统容许高优先级的县城插队。这样有可能致使低优先级线程产生饥饿。但若是锁是公平的,知足先来后到的原则,那么饥饿就不会产生,无论新来的线程优先级多高,要想得到资源,就必须乖乖排队。那么全部的线程都有机会执行。

无障碍

    无障碍是一种最弱的非阻塞调度。两个线程若是是无障碍的执行,那么他们不会由于临界区的问题致使一方被挂起。你们均可以大摇大摆进入临界区工做。那么若是你们都修改了共享数据怎么办呢?对于无障碍的线程来讲,一旦出现这种状况,当前线程就会当即对修改的数据进行回滚,确保数据安全。但若是没有数据竞争发生,那么线程就能够顺利完成本身的工做,走出临界区。 
    若是阻塞控制的方式比喻成悲观策略。也就是说系统认为两个线程之间颇有可能发生不幸的冲突,所以,保护共享数据为第一优先级。相对来讲,非阻塞的调度就是一种乐观策略,他认为多线程之间颇有可能不会发生冲突,或者说这种几率不大,可是一旦检测到冲突,就应该回滚。 
    从这个策略来看,无障碍的多线程程序不必定能顺利执行。由于当临界区的字眼存在严重的冲突时,全部线程可能都进行回滚操做,致使没有一个线程能够走出临界区。因此咱们但愿在这一堆线程中,至少能够有一个线程能够在有限时间内完成本身的操做,至少这能够保证系统不会再临界区进行无线等待。 
    一种可行的无障碍实现能够依赖一个“一致性标记”来实现。线程在操做以前,先读取并保持这个标记,在操做完后,再次读取,检查这个标记是否被修改过,若是先后一致,则说明资源访问没有冲突。若是不一致,则说明资源可能在操做过程当中与其余写线程冲突,须要重试操做。任何对保护资源修改以前,都必须更新这个一致性标记,表示数据不安全。

无锁

    无锁的并行都是无障碍的。在无锁的状况下,全部的线程都能尝试对临界区的资源进行访问,但不一样的是,无锁的并发保证必然有一个线程可以在有限步内完成操做离开临界区。 
    在无锁的调度中,一个典型的特色是可能会包含一个无穷循环。在这个循环中线性不断尝试修改共享数据。若是没有冲突,修改为功,那么线程退出,不然尝试从新修改。但不管如何,无锁的并行总能保证有一个线程能够胜出,不至于全军覆没。至于临界区中竞争失败的线程,则不断重试。若是运气很差,老是不成功,则会出现相似饥饿的现象,线程会中止不前。

无等待

    无锁是要求至少有一个线程在有限步内完成操做,而无等待则是在无锁的基础之上进一步扩展。他要求全部线程都必须在有限步内完成操做。这样就不会引发饥饿问题。若是限制这个步骤上限,还能够分为有界无等待和线程无关的无等待几种,它们之间的区别只是对循环次数的限制不一样。 
    一种典型的无等待结构是RCU(read-copy-update)。它的基本思想是,对数据的读能够不加控制,所以全部读线程都是无等待的,它们既不会被锁定等待也不会引发任何冲突。但在写数据时,先取得原始数据的副本,接着只修改副本数据,修改完后,在合适的时机回写数据。

线程状态

    要获取状态能够经过线程(Thread)的getState()来获取状态的值。例如,获取当前线程的状态就可使用Thread.currentThrad().getState()来取值。该方法返回的类型是一个枚举类型,包含了new、runnable、blocked、waiting、timed_waiting、terminated这些值。

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING,
    TERMINATED;
}
  • new:状态表示刚刚建立的线程,这种线程尚未执行。等到线程的start()方法调用时,才表示线程开始执行。当线程执行时,处于runnable状态.
  • runnable:线程所需的一切资源都已经转杯好了。若是线程在执行中遇到了synchronized同步块,就会进入blocked阻塞状态,
  • blocked:此时的状态为线程会暂停执行,直到得到请求的锁。
  • waiting:进入一个无时间限制的等待。等待特殊事件,如经过wait()方法等待的线程在等待notify方法,而经过join()方法等待的线程则会等待目标线程终止。一旦等到了指望的事件,线程就会再次执行,进入runnable状态。
  • timed_waiting:进入一个有时间限制的等待。等待特殊事件,如经过wait()方法等待的线程在等待notify方法,而经过join()方法等待的线程则会等待目标线程终止。一旦等到了指望的事件,线程就会再次执行,进入runnable状态。
  • terminated:当线程执行完毕后,则进入terminated状态,表示结束。

这里写图片描述

相关文章
相关标签/搜索