关耳er  

一、Java当中线程状态有哪些

  线程的五大状态分别为:创建状态(New)、就绪状态(Runnable)、运行状态(Running)、阻塞状态(Blocked)、死亡状态(Dead)。

  

(1)新建状态:即单纯地创建一个线程,创建线程有三种方式,在我的博客:线程的创建,可以自行查看!

(2)就绪状态:在创建了线程之后,调用Thread类的start()方法来启动一个线程,即表示线程进入就绪状态!

(3)运行状态:当线程获得CPU时间,线程才从就绪状态进入到运行状态!

(4)阻塞状态:线程进入运行状态后,可能由于多种原因让线程进入阻塞状态,如:调用sleep()方法让线程睡眠,调用wait()方法让线程等待,调用join()方法、suspend()方法(它现已被弃用!)以及阻塞式IO方法。

(5)死亡状态:run()方法的正常退出就让线程进入到死亡状态,还有当一个异常未被捕获而终止了run()方法的执行也将进入到死亡状态!

二、多线程中的常用方法

1、public void start()  使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

2、public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

3、public final void setName(String name) 改变线程名称,使之与参数 name 相同

4、public final void setPriority(int piority) 更改线程的优先级。

5、public final void setDaemon(boolean on) 将该线程标记为守护线程或用户线程。

6、public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。

7、public void interrupt() 中断线程。

8、public final boolean isAlive() 测试线程是否处于活动状态。

9、public static void static yield() 暂停当前正在执行的线程对象,并执行其他线程。

10、public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

11、public static Thread currentThread() 返回对当前正在执行的线程对象的引用。

  

 三、线程状态流程图

  

  新建(new)新创建了一个线程对象。

  可运行(runnable)线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

  运行(running)可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

  阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

  等待阻塞:

    运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。

  同步阻塞:

    运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。

  其他阻塞:

    运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

  死亡(dead):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

四、volatile关键字有什么用途,和Synchronize有什么区别

1、volatile的用法与作用

  用来修饰变量,例如:Thread类里面的表示名字的字符数组

  

  作用:保证数据的可见性和有序性,但它并不能保证数据的原子性

  原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

  因为volatile不能保证原子性,所以就引出了下面一个关键字 synchronized

2、synchronized的用法与作用

  它用于修饰代码块和方法,可以弥补volatile关键字的不足,即它能保证对数据操作的原子性,在多个线程对数据进行操作时,保证线程的安全

  synchronized提供了两种特性:互斥性和可见性

    互斥性:如果将锁加在某个变量上,则每次只有一个线程能够使用该共享数据,直到该线程使用完才会将该共享数据释放,供其它线程使用。

    可见性:线程在得到锁时读入副本,释放时写回内存。

3、volatile与synchronized的区别

  修饰对象不同,volatile用于修饰变量,synchronized用与对语句和方法加锁;

  各自作用不同,volatile保证数据的可见性和有序性,但它并不能保证数据的原子性,synchronized可以保证原子性;

  是否线程堵塞,volatile不会造成线程堵塞,而synchronized会造成线程堵塞;

4、什么是脏读,为什么发生脏读

  脏读是指当一个事务正在访问数据,并且对数据进行了修改。而这种修改还没有提交到数据库中,这时,另外一个事务也访问了这个数据,然后使用了这个数据。

五、先行发生原则(Happens-Before)

  先行发生原则(Happens-Before)是判断数据是否存在竞争、线程是否安全的主要依据。
  先行发生是Java内存,模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,那么操作A产生的影响能够被操作B观察到。
Java内存模型中存在的天然的先行发生关系:

  1. 程序次序规则

    同一个线程内,按照代码出现的顺序,前面的代码先行于后面的代码,准确的说是控制流顺序,因为要考虑到分支和循环结构。

  2. 管程锁定规则

    一个unlock操作先行发生于后面(时间上)对同一个锁的lock操作。

  3. volatile变量规则

    对一个volatile变量的写操作先行发生于后面(时间上)对这个变量的读操作。

  4. 线程启动规则

    Thread的start( )方法先行发生于这个线程的每一个操作。

  5. 线程终止规则

    线程的所有操作都先行于此线程的终止检测。可以通过Thread.join( )方法结束、Thread.isAlive( )的返回值等手段检测线程的终止。 

  6. 线程中断规则

    对线程interrupt( )方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupt( )方法检测线程是否中断

  7. 对象终结规则

    一个对象的初始化完成先行于发生它的finalize()方法的开始。

  8. 传递性

    如果操作A先行于操作B,操作B先行于操作C,那么操作A先行于操作C。
  总结:
    一个操作“时间上的先发生”不代表这个操作先行发生;一个操作先行发生也不代表这个操作在时间上是先发生的(重排序的出现)。
    时间上的先后顺序对先行发生没有太大的关系,所以衡量并发安全问题的时候不要受到时间顺序的影响,一切以先行发生原则为准。

六、并发编程线程安全三要素

原子性(Synchronized, Lock)

  即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

  在Java中,基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

有序性(Volatile,Synchronized, Lock)

  即程序执行的顺序按照代码的先后顺序执行。

  在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

可见性(Volatile,Synchronized,Lock)

  指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

  当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取共享变量时,它会去内存中读取新值。

  普通的共享变量不能保证可见性,因为普通共享变量被修改后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

七、进程和线程间调度算法

1、先来先服务(队列)

  先来先服务(FCFS)调度算法是一种最简单的调度算法,该算法既可用于作业调度,也可用于进程调度。当在作业调度中采用该算法时,每次调度都是从后备作业队列中选择一个或多个最先进入该队列的作业,将它们调入内存,为它们分配资源、创建进程,然后放入就绪队列。在进程调度中采用FCFS算法时,则每次调度是从就绪队列中选择一个最先进入该队列的进程,为之分配处理机,使之投入运行。该进程一直运行到完成或发生某事件而阻塞后才放弃处理机。
  缺点:比较有利于长作业,而不利于短作业。 有利于CPU繁忙的作业,而不利于I/O繁忙的作业。

2、最短优先(优先队列)

  最短优先调度算法是指对短作业或短进程优先调度的算法。它们可以分别用于作业调度和进程调度。短作业优先(SJF)的调度算法是从后备队列中选择一个或若干个估计运行时间最短的作业,将它们调入内存运行。而短进程优先(SPF)调度算法则是从就绪队列中选出一个估计运行时间最短的进程,将处理机分配给它,使它立即执行并一直执行到完成,或发生某事件而被阻塞放弃处理机时再重新调度。
  缺点:长作业的运行得不到保证。

3、优先权调度算法

  3.1 优先权调度算法的类型

    为了照顾紧迫型作业,使之在进入系统后便获得优先处理,引入了最高优先权优先(FPF)调度算法。此算法常被用于批处理系统中,作为作业调度算法,也作为多种操作系统中的进程调度算法,还可用于实时系统中。当把该算法用于作业调度时,系统将从后备队列中选择若干个优先权最高的作业装入内存。当用于进程调度时,该算法是把处理机分配给就绪队列中优先权最高的进程,这时,又可进一步把该算法分成如下两种。
    1) 非抢占式优先权算法
      在这种方式下,系统一旦把处理机分配给就绪队列中优先权最高的进程后,该进程便一直执行下去,直至完成;或因发生某事件使该进程放弃处理机时,系统方可再将处理机重新分配给另一优先权最高的进程。这种调度算法主要用于批处理系统中;也可用于某些对实时性要求不严的实时系统中。
    2) 抢占式优先权调度算法
      在这种方式下,系统同样是把处理机分配给优先权最高的进程,使之执行。但在其执行期间,只要又出现了另一个其优先权更高的进程,进程调度程序就立即停止当前进程(原优先权最高的进程)的执行,重新将处理机分配给新到的优先权最高的进程。因此,在采用这种调度算法时,是每当系统中出现一个新的就绪进程i 时,就将其优先权Pi与正在执行的进程j 的优先权Pj进行比较。如果Pi≤Pj,原进程Pj便继续执行;但如果是Pi>Pj,则立即停止Pj的执行,做进程切换,使i 进程投入执行。显然,这种抢占式的优先权调度算法能更好地满足紧迫作业的要求,故而常用于要求比较严格的实时系统中,以及对性能要求较高的批处理和分时系统中。

  3.2 高响应比优先调度算法

    在批处理系统中,短作业优先算法是一种比较好的算法,其主要的不足之处是长作业的运行得不到保证。如果我们能为每个作业引入前面所述的动态优先权,并使作业的优先级随着等待时间的增加而以速率a 提高,则长作业在等待一定的时间后,必然有机会分配到处理机。该优先权的变化规律可描述为:  

        

    由于等待时间与服务时间之和就是系统对该作业的响应时间,故该优先权又相当于响应比RP。据此,又可表示为:  

        

  由上式可以看出:
    (1) 如果作业的等待时间相同,则要求服务的时间愈短,其优先权愈高,因而该算法有利于短作业。
    (2) 当要求服务的时间相同时,作业的优先权决定于其等待时间,等待时间愈长,其优先权愈高,因而它实现的是先来先服务。
    (3) 对于长作业,作业的优先级可以随等待时间的增加而提高,当其等待时间足够长时,其优先级便可升到很高,从而也可获得处理机。
  简言之,该算法既照顾了短作业,又考虑了作业到达的先后次序,不会使长作业长期得不到服务。因此,该算法实现了一种较好的折衷。当然,在利用该算法时,每要进行调度之前,都须先做响应比的计算,这会增加系统开销。

4、基于时间片的轮转调度算法

  4.1 时间片轮转发

    在早期的时间片轮转法中,系统将所有的就绪进程按先来先服务的原则排成一个队列,每次调度时,把CPU 分配给队首进程,并令其执行一个时间片。时间片的大小从几ms 到几百ms。当执行的时间片用完时,由一个计时器发出时钟中断请求,调度程序便据此信号来停止该进程的执行,并将它送往就绪队列的末尾;然后,再把处理机分配给就绪队列中新的队首进程,同时也让它执行一个时间片。这样就可以保证就绪队列中的所有进程在一给定的时间内均能获得一时间片的处理机执行时间。换言之,系统能在给定的时间内响应所有用户的请求。

  4.2 多级反馈队列调度算法

    前面介绍的各种用作进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程,而且如果并未指明进程的长度,则短进程优先和基于进程长度的抢占式调度算法都将无法使用。而多级反馈队列调度算法则不必事先知道各种进程所需的执行时间,而且还可以满足各种类型进程的需要,因而它是目前被公认的一种较好的进程调度算法。在采用多级反馈队列调度算法的系统中,调度算法的实施过程如下所述。
    (1) 应设置多个就绪队列,并为各个队列赋予不同的优先级。第一个队列的优先级最高,第二个队列次之,其余各队列的优先权逐个降低。该算法赋予各个队列中进程执行时间片的大小也各不相同,在优先权愈高的队列中,为每个进程所规定的执行时间片就愈小。例如,第二个队列的时间片要比第一个队列的时间片长一倍,……,第i+1个队列的时间片要比第i个队列的时间片长一倍。
    (2) 当一个新进程进入内存后,首先将它放入第一队列的末尾,按FCFS原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;如果它在一个时间片结束时尚未完成,调度程序便将该进程转入第二队列的末尾,再同样地按FCFS原则等待调度执行;如果它在第二队列中运行一个时间片后仍未完成,再依次将它放入第三队列,……,如此下去,当一个长作业(进程)从第一队列依次降到第n队列后,在第n 队列便采取按时间片轮转的方式运行。
    (3) 仅当第一队列空闲时,调度程序才调度第二队列中的进程运行;仅当第1~(i-1)队列均空时,才会调度第i队列中的进程运行。如果处理机正在第i队列中为某进程服务时,又有新进程进入优先权较高的队列(第1~(i-1)中的任何一个队列),则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回到第i队列的末尾,把处理机分配给新到的高优先权进程。

5、电梯调度算法

  高层建筑中电梯请求不断地到来,控制电梯的计算机能够很容易地跟踪顾客按下请求的顺序。如果使用先来先服务算法调度,同时如果电梯负载很重,那么大部分时间电梯将停留在电梯的中部区域,而电梯两端区域的请求将不得不等待,直到负载中的统计波动使得中部区域没有请求位置,这样导致远离中部区域的请求得到的服务很差。因此获得最小响应时间的目标和公平性之间存在着冲突。
大多数电梯使用电梯算法来协调效率和公平性这两个相互冲突的目标。电梯算法电梯保持按一个方向移动,直到在那个方向上没有请求位置,然后改变方向。
电梯算法(elevation algorithm)需要软件维护一个二进制位,即当前方向位:向上(up)或向下(down)。当一个请求处理完成之后,电梯的驱动程序检查该位,如果是up,电梯移至下一个更高的未完成的请求。如果更高的位置没有未完成的请求,则方向位取反。当方向位设置为down时,同时存在一个低位置的请求,则移向该位置。

  现在我们明白了,电梯的上下箭头按钮是为了告诉电梯你想向上还是向下去),而不是让电梯向上还是向下。

  举例:电梯在上行,5楼有上召和下召。电梯会停5楼,但它是为上召服务的,所以下召灯还会保持点亮。然后启动向上,直到服务完上行的所有请求。转下行,到五楼时还是会停。这时是服务5楼下召的。

  电梯处理请求规则:

    电梯有移动方向,各楼层的请求有请求方向,这里维护一个请求表(记录请求ID,请求方向,该请求的停靠楼层)。因为电梯会按照移动方向移动,直到该方向没有请求(请求包括请求ID和停靠楼层的请求),所以不会根据请求方向突然改变电梯的移动方向。因此,电梯在移动过程中只处理与“电梯移动方向”相同的“请求方向”的请求。如电梯向下移动,只处理向下的请求,且该请求的方向也向下(停靠楼层请求无方向)。

 

posted on 2020-03-27 21:11  关耳er  阅读(142)  评论(0编辑  收藏  举报