zoukankan      html  css  js  c++  java
  • 多线程基本知识总结(仅仅是多线程,没有讲到线程同步,线程通讯)

    一。线程的一些基本知识。
    进程与线程
    所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中就是一个进程,当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。

    进程(process)
    当一个程序进入内存运行即变成一个进程,进程处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调用的独立单位,进程切换开销大。

    多进程
    在操作系统中,能同时运行多个任务程序。

    进程包含三大特征
    1,独立性:
    进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间,没有经过进程本身允许的情况下,一个用户不可以直接访问其他进程的地址空间。
    2,动态性:
    进程与程序的区别在于程序只是个静态的指定集合,而进程是一个正在系统中活动的指定集合,在进程中加入了时间的概念,进程具有自己的生命周期和各种不同的状态,这些状态在程序中是不具备的。
    3,并发性:
    多个线程可以在单个处理器是并发执行多个进程,进程之间,不会互相影响。

    注:
    对于一个CPU而言,它在某个时间点上只能执行一个程序,就是说只能运行一个进程。CPU不断在这些进程之间轮回切换,CPU的执行速度相对于我们的感觉太快,我们感觉不到,所以CPU在多个进程之间轮回执行,但我们人类感觉好像多个进程同时执行。

    线程(Thread)
    线程被称为轻量级的进程,线程是进程的执行单元,就像进程在操作系统中的地位一样。线程在程序中是独立的,并发的执行的流。当进程被初始化后,主线程就被创建了,一般的应用程序来说,通常仅要求有一个主线程,但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程,每条线程是相互独立的。

    注:
    总而言之,一个程序运行后,至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程。

    多线程(multithreading)
    在同一个应用程序中,有多个顺序执行流同时执行。

    线程比进程具有更高的性能,这是由于同一个进程中的线程都有共性,多个线程将共享同一个进程的虚拟空间,-线程共享的环境包括进程代码段,进程的公有数据等,利用这些共享的数据,线程很容易实现相互之间的通信。

    当操作系统创建一个进程时,该进程必须分配独立的内存空间,分配大量相关资料,但创建一个线程简单的多,因此使用多线程来实现并发比使用多进程来实现并发的性能要高得多。

    多线程的优点
    1,进程之间不能共享内存,但线程之间共享内存非常容易。
    2,系统创建进程需要为该进程重新分配系统资源,但创建线程代价要小的多,因此使用多线程来实现多个任务并发比多进程要效率高。
    3,Java语言中内置多线程功能的支持,而不是单纯的作为底层的操作系统的调动方式,从而简化了Java的多线程编程。

    二。线程的启动和创建
    继承Thread类创建和启动线程
    1,定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务,run方法也被称为线程执行体。
    2,创建Thread类子类的实例,即创建了线程的对象。
    3,用线程对象的start方法来启动该线程。

    start()方法
    使该线程开始执行,Java虚拟机调用该线程的run方法。
    getName()方法
    返回该线程的名称
    setName(String name)方法
    修改该线程的名称
    interrupt();
    中断线程
    sleep(long millis);
    在指定的毫秒内让当前正在执行的线程休眠.

    1,实现Runable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的执行体。
    2,创建Runable实现类的实例,并以此实例作为Thread类的目标来创建Thread类的目标创建Thread对象,该Thread对象才是真正的线程对象。

    Thread类和Runable接口创建线程的对比
    继承Thread类方式的多线程的优点:
    编写简单,如果访问当前线程,无需使用Thread.currentThread方法,直接使用this,即可获得当前线程。
    继承Thread类方式的多线程缺点:
    因为线程已经继承了Thread类,所以不能再继承其他的类。

    实现Runable接口的优点:
    线程只是实现了Runable接口,还可以继承其他的类,在这种方式下,可以多个线程共享同一个目标(target)对象,所以非常适合多个线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,体现了面向对象的思想。
    实现Runable接口的缺点:
    编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread方法。

    三。线程的生命周期
    当线程被创建并启动以后,它既不是一启动就进入了执行的状态,也不是一直处于执行状态,在线程的生命周期里,它要经过新建,就绪,运行,阻塞,死亡五种状态。尤其是当线程启动以后,它不能一直霸占着CPU独立运行,所以CPU多条线程之间切换,于是线程状态也会多次在运行和阻塞之间切换。

    新建或就绪状态
    启动线程使用start方法,而不是run方法,永远不用调用线程对象的run方法,调用start方法来启动线程系统会把该run方法当成线程执行体来处理。但如果直接调用线程对象的run方法,则run方法立即会被执行,而且在run方法返回之前其他线程无法并发执行,也就是说系统吧线程对象当成一个普通的对象,而run方法也是一个普通方法,而不是线程执行体。

    当线程对象调用了start方法之后,该线程处于就绪状态,JVM会为创建方法调用栈和线程计数器处于这个状态中的线程,并没有开始运行,它只是表示该线程可以运行了,至于该线程何时开始运行,取决于JVM里线程调度器的调度。

    运行和阻塞状态
    如果处于就绪状态的线程获得了CPU资源,开始执行run方法的执行体,则该线程处于运行状态,当一条线程开始运行后,它不可能一直处于运行状态,线程在运行的过程中,需要被中断,目的是使其他线程获得执行的机会,当发生以下情况下,线程将会进入阻塞状态;
    1,线程调用sleep方法主动放弃所占有的CPU资源,调用wait,join方法也是线程进入阻塞状态。
    2,线程调用一个阻塞式IO方法,该方法返回之前该线程被阻塞。
    3,线程试图获得一个同步监视器,但该同步监视器正被其他线程所占据。
    4,线程在等待某个通知(ntify,ntifyAll方法 在Objiet类里)。
    5,程序调用了线程的suspend方法,将线程挂起暂停),不过这个方法容易导致死锁,所以程序尽量避免该方法。
    线程重新进入就绪状态有以下情况:
    1,调用sleep方法的线程经过指定的时间。
    2,线程调用阻塞式IO方法已经返回。
    3,线程成功的获得了试图取得同步监视器。
    4,线程正在等待某个通知,其他线程发出了通知。
    5,处于挂起状态的线程被调用了resume方法恢复。

    注:
    阻塞状态也叫不可运行状态
    调用yield方法可以让当前运行的线程转入就绪状态。

    线程会在以下三种方式之一结束线程,处于死亡状态:
    1,run方法执行完成,线程正常结束。(当需要结束的时候可以使用return结束该线程)
    2,线程抛出一个未捕获的Exception或Error。
    3,直接调用线程的stop方法来结束该线程,该方法容易导致死锁,通常不推荐用。

    isAlive方法
    测试某条线程是否已经死亡,当线程处于就绪,运行,阻塞三种状态时,该方法返回true,当该线程处于创建,死亡两种状态时,该方法返回false。

    注:
    不要试图对一个已经死亡的线程调用start方法,使它重新启动,该线程将不可再次作为线程执行,它会抛出IllegaThreadStateException异常。

    不要让处于死亡状态的线程调用start方法,程序只能对新建状态的线程调用start方法,对新建状态的线程两次调用start方法也是错误的。

    join()方法
    等待被join的线程执行完成。
    join(long millis)方法
    等join的新车的时间最长为minllis毫秒,如果在millis毫秒内被join的线程还没有执行结束,则不再等待。
    join(long millis ,int nanos)方法
    等待被join的时机最长为millis毫秒加nanos纳秒。


    setPriority()和方法:
    来设值和取值,
    返回线程的优先级,其中setPriority可以是个整数,范围是一到十之间,也可以是MAX_PRIORITY ,MIN_PRIORITY ,NORM_PRIORITY 三个静态常量。
    getPriority()方法
    返回线程的优先级。每个线程执行时,都具有一定的优先级,优先级高的线程获得较多的执行机会,而优先级低的线程则获得较小的执行机会。

    后台线程(DaemonThread)
    有一种线程它是在后台运行的,它的任务是为其他线程提供服务,这种线程称为后台线程,又称为守护线程,又称为精灵线程。JVM的垃圾回收线程就是典型的后台线程。后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。调用Thread对象的setDaemon(blooean on)方法(参数传true),可将指定线程设置为后台线程,isDaemon()判断该线程是否为后台线程,如果该线程是守护线程,则返回 true;否则返回 false。

    sleep(long millis)方法(线程睡眠)
    让当前正在执行的线程暂停并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响。

    注:
    当前线程调用sleep方法,进入阻塞状态后,在sleep时间段内,该线程不会获得执行的机会,即使系统中没有其他可运行的线程,处于sleep时间段里的线程也不会运行。因此sleep常用来暂停程序的执行,


    yield()方法(线程让步)
    yield方法和sleep有点相似的方法,让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入就绪状态,yield方法只是让当前线程暂停一下,让系统的线程调度器从新调度一次,当某个线程调用了yield方法暂停之后,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程才会获得执行的机会


    sleep方法和yield方法的区别
    1,sleep方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级,但yield方法只会给优先级相同或优先级更高的线程执行机会。
    2,sleep方法会将线程转入阻塞状态,直到经过阻塞时间,才会转入就绪状态,而yield方法不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态,因此完全有可能某个线程用yield方法暂停之后立即再次获得处理器资源被执行。
    3,sleep方法声明抛出 InterruptedException 异常。所有调用sleep方法时,要么扑捉该异常,要么显示声明抛出该异常。而yield方法则没有声明抛出任何异常。
    4,sleep方法比yield方法有更好的可移植性,通常不用依靠yield方法来控制并发线程的执行。


    interrupt()
    使该线程中断,如果一个线程抛出异常,可以用interrupt在catch里中断该线程.


    Thread()
              分配新的 Thread 对象。
    Thread(Runnable target)
              分配新的 Thread 对象。
    Thread(Runnable target, String name)
              分配新的 Thread 对象。
    Thread(String name) 


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

     String getName()
              返回该线程的名称。

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

     int getPriority()
              返回线程的优先级。
     void setPriority(int newPriority)
              更改线程的优先级。

     void interrupt()
              中断线程。

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

     boolean isDaemon()
              测试该线程是否为守护线程。

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

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

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

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

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

    --------------------------------------------------------------下面是另一份总结--------------------------------------------------------------------------------------------

    多线程
    线程:是指进程中的一个执行流程。
    线程与进程的区别:每个进程都需要操作系统为其分配独立的内存地址空间,而同一进程中的所有线程在同一块地址空间中工作,这些线程可以共享同一块内存和系统资源。


    如何创建一个线程?

    创建线程有两种方式,如下:
    1、 扩展java.lang.Thread类
    2、 实现Runnable接口
    Thread类代表线程类,它的两个最主要的方法是:
    run()——包含线程运行时所执行的代码
    Start()——用于启动线程

    一个线程只能被启动一次。第二次启动时将会抛出java.lang.IllegalThreadExcetpion异常

    新建状态:用new语句创建的线程对象处于新建状态,此时它和其它的java对象一样,仅仅在堆中被分配了内存
    就绪状态:当一个线程创建了以后,其他的线程调用了它的start()方法,该线程就进入了就绪状态。处于这个状态的线程位于可运行池中,等待获得CPU的使用权
    运行状态:处于这个状态的线程占用CPU,执行程序的代码
    阻塞状态:当线程处于阻塞状态时,java虚拟机不会给线程分配CPU,直到线程重新进入就绪状态,它才有机会转到运行状态。
    阻塞状态分为三种情况:
    1、 位于对象等待池中的阻塞状态:当线程运行时,如果执行了某个对象的wait()方法,java虚拟机就回把线程放到这个对象的等待池中
    2、 位于对象锁中的阻塞状态,当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他的线程占用,JVM就会把这个线程放到这个对象的琐池中。
    3、 其它的阻塞状态:当前线程执行了sleep()方法,或者调用了其它线程的join()方法,或者发出了I/O请求时,就会进入这个状态中。

    死亡状态:当线程退出了run()方法,就进入了死亡状态,该线程结束了生命周期。
               或者正常退出
               或者遇到异常退出
               Thread类的isAlive()方法判断一个线程是否活着,当线程处于死亡状态或者新建状态时,该方法返回false,在其余的状态下,该方法返回true.

    线程调度
    线程调度模型:分时调度模型和抢占式调度模型
    JVM采用抢占式调度模型。
    所谓的多线程的并发运行,其实是指宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务。
    (线程的调度不是跨平台,它不仅取决于java虚拟机,它还依赖于操作系统)

    如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下的办法之一
    1、 调整各个线程的优先级
    2、 让处于运行状态的线程调用Thread.sleep()方法
    3、 让处于运行状态的线程调用Thread.yield()方法
    4、 让处于运行状态的线程调用另一个线程的join()方法

    调整各个线程的优先级
    Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和读取优先级。
    如果希望程序能够移值到各个操作系统中,应该确保在设置线程的优先级时,只使用MAX_PRIORITY、NORM_PRIORITY、MIN_PRIORITY这3个优先级。

    线程睡眠:当线程在运行中执行了sleep()方法时,它就会放弃CPU,转到阻塞状态。
    线程让步:当线程在运行中执行了Thread类的yield()静态方法时,如果此时具有相同优先级的其它线程处于就绪状态,那么yield()方法将把当前运行的线程放到运行池中并使另一个线程运行。如果没有相同优先级的可运行线程,则yield()方法什么也不做。
    Sleep()方法和yield()方法都是Thread类的静态方法,都会使当前处于运行状态的线程放弃CPU,把运行机会让给别的线程,两者的区别在于:
             1、sleep()方法会给其他线程运行的机会,而不考虑其他线程的优先级,因此会给较低线程一个运行的机会;yield()方法只会给相同优先级或者更高优先级的线程一个运行的机会。
    2、当线程执行了sleep(long millis)方法后,将转到阻塞状态,参数millis指定睡眠时间;当线程执行了yield()方法后,将转到就绪状态。
              3、sleep()方法声明抛出InterruptedException异常,而yield()方法没有声明抛出任何异常
              4、sleep()方法比yield()方法具有更好的移植性

    等待其它线程的结束:join()
              当前运行的线程可以调用另一个线程的 join()方法,当前运行的线程将转到阻塞状态,直到另一个线程运行结束,它才恢复运行。

    定时器Timer:在JDK的java.util包中提供了一个实用类Timer, 它能够定时执行特定的任务。

  • 相关阅读:
    天气预报FLEX版本
    关于“ORA01000: 超出打开游标的最大数”
    WIN7(x64) IIS7.5 404.17错误:请求的内容似乎是脚本,因而将无法由静态文件处理程序来处理。
    解决GDI+中“内存不足”问题
    Stack Overflow Exception
    清洁的Javascript
    设置SQL Server数据库中某些表为只读的多种方法
    程序员肿么了?为何总被认为是“屌丝”
    jquery datepicker 显示12个月份
    apache2.4配置虚拟主机随记
  • 原文地址:https://www.cnblogs.com/hudingbo/p/3541085.html
Copyright © 2011-2022 走看看