第十四章 多线程编程
1、单任务和多任务:
单任务:
指系统每次只能做一件事情;
例如:微软最早的Dos操作系统就是单任务系统
多任务:
指系统同事能做多件事;
在多任务操作系统里,一个任务一般对应一个进程,也可以包含多个进程。
2、进程
指每个独立程序在计算机中的一次执行活动;
线程
指一个进程中可以执行多个任务,每个任务通常称为一个线程。这种只能执行一个任务的程序称为单线程程序,二能同时执行多个线程的程序又称为多线程程序。
3、单线程程序:单线程程序在同时执行一个任务
在此之前,编写的JAVA程序都是单线程程序。他是从main方法开始一行一行代码往下执行的,执行完后回到main方法,结束整个程序。在运行单线程程序时,JVM会自动启动一个进程,这个进程称为主进程;接着JVM找到程序的入库点main方法;然后执行main()。这时会自动产生一个线程,这个程序称为主线程。当main()方法执行完毕,主线程退出,主进程退出,程序执行结束。
1、 多线程程序:多线程程序在同一时间内可以执行多个任务。
在运行多线程程序时,JVM也会自动启动一个主进程和一个主线程。而在执行main()方法过程中,可根据需要手动创建线程来执行多个任务,这种手动创建的线程称为子线程。当main()方法执行完毕,主线程退出,子线程也会强制退出,主进程退出,程序执行结束。
2、 进程和线程的区别:
1) 每个进程都有独立的代码和数据空间(进程上下文),进程间的切换开销大。
2) 同个进程内的多个线程共享相同的代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程间的切换开销小。
3、 多线程的优点:
1) 改善应用程序响应。
这对图形界面程序更有意义。当一个操作耗时长时,整个系统都会等待这个操作,此时程序不能相应键盘、鼠标、菜单等操作。而多线程技术,将耗时的操作放置到一个子线程中执行,而界面任然响应用户的操作,这样可以增强用户的体验。
2) 提高计算机系统CPU的利用率。
多现场可以充分利用现代计算机的多CPU运算能力的热点,从而节省响应时间。
3) 改善程序结构。
一个既长又复杂的程序可以考虑分为多个子线程,称为几个独立或半独立的子任务,这是的程序有利于理解和修改。
7、JAVA中提供了两种创建子线程的方式:
第一种:
1) 定义类实现Java.long.Runnable接口;
2) 重写接口中的run方法;
3) 通过Thread类实例化线程对象
4) 将Runnable接口的实现类对象作为实际参数传递给Thread类的构造器参数中
5) 调用线程对象的start方法启动线程。
第二种:
1) 自定义类继承Thread类;
2) 重写Thread类的run方法;
3) 通过Thread类实例化线程对象
4) 将自定义类对象作为实际参数传递给Thread类的构造器参数;
5) 调用线程对象的start方法启动线程。
这两种创建线程的方式中,建议使用第一种方式,因为采用实现接口的方式可以避免由于Java的单一继承带来的局限,有利于程序代码的健壮性。
8、线程的生命周期:
一个线程从他的创建到销毁的过程中,会经历不同的阶段。线程的生命周期是一个比较复杂的过程,要想掌握线程的使用,就要很好的理解她的生命周期。
一个线程创建之后,它总是出于其生命周期的6种状态之一。这六种状态分别是:
1) 新建状态(NEW):至今尚未启动的线程处于这种状态
2) 可运行状态(RUNNABLE):正在JAVA虚拟机中执行的线程处于这种状态
3) 阻塞状态(BOLOCKED):受阻塞并等待某个监视器的线程处于这种状态
4) 等待状态(WATTING):无限期的等带另一个线程来执行某一个特定操作的线程处于这种状态
5) 超时等待状态(TIMED_WATTING):等待另一个线程来执行取决于制定等待时间的操作的线程处于这种状态
6) 终止状态(TERMINATED):已退出的线程处于这种状态。
9、各种状态的特征及状态的相互转换关系:
1、 新创建线程
当创建Thread类(或子类)的对象时,就意味着该线程处于新建状态。当一个线程处于新状态时,它的线程体中的代码并未被执行
2、 可运行的线程:
一旦调用了该线程对象的start()方法,该线程便是可运行线程、但可运行的线程并不一定立刻就在执行,需要操作系统为该线程赋予执行的时间片。
当线程里的代码开始执行时,改线程变开始运行了。一单线程开始运行,他不一定始终保持运行状态,因为操作系统随时可能会被打断他的执行,一边其他线程得到执行机会。
3、 被阻塞和等待状态下的线程:
当一个线程被阻塞或等待状态时,他暂时处于停止,不会执行线程体内的代码,消耗很少的资源。
1) 当一个可运行的线程在获得某个对象锁时,若该独享锁被别的线程占用,则JVM会把该线程池中,这种状态也叫同步阻塞状态。
2) 当一个线程需要等待另一个线程来通知它的调度时,他就进入等待状态。这通常时通过运用object.wait()方法或Thread.join()方法来使一个线程进入等待状态的。在实际应用开发中,阻塞和等待状态区别不大。
3) 有几种带超时值得方法会导致线程进入定时等待状态。这种状态维持到超时过期或梳打适当的通知为止。这些方法主要包括Object.wait(long timeout)、Thead.Join(long timeout)和Thead.sleep(long millis)。带可运行的线程并不是一定立刻就在
4、被终止的线程:
基于以下原因,线程会被终止
1) 由于run()方法正常执行完毕而自然终止;
2) 由于没有捕获到异常终止了run()方法的执行而直到线程突然死亡。
10、如何判断某个线程是否还活着;
可以调用Thread.isAlive()方法。如果线程处于可运行状态或阻塞状态,他返回true;如果线程处于新建状态或终止,它将返回false。
11、如何让某个线程睡眠
在线程体调用Thread.sleep()方法会使当前线程进入睡眠状态。当线程睡眠相应时间后会苏醒,重新进入可运行状态。在实际开发应用中,为了调整线程的执行顺序,可以通过线程睡眠的方式完成。
12、如何让某个线程挂起
可以调用Thread.yield()方法会暂停当前正在执行的线程对象,把执行机会让给相同或更高优先级的线程。
13、如何让某个线程加入
有时需要线程间的接力完成某项任务,
14、线程的调度和优先级
线程的调度时让JVM对多个线程进行系统级的协调,以避免因多个线程争用有限的资源而导致应用程序死机或崩溃。
Java实现线程调度的方式是采用优先级策略。Java将线程的优先级分为10个等级,分别为是1~10之间的数字来表示,数字越大表明线程的优先级越高。相应的,在Thread类中定义了表示线程最低、最高和普通优先级的镜头成员变量。
MIN_PRIORITY:表示1
MAX_ PRIORITY:表示10
NOPMAL_ PRIORITY:表示5
当一个线程被创建是,其默认的线程优先级是5,JVM提供了一个线程的调度器来监控应用程序启动后进入可运行状态的所有线程。优先级高的线程或获得较多的运行机会。设置线程优先级的方法是setPriority(),获取线程的优先级方法是getPriority();
15、线程同步
大多数需要运行多线程的应用程序中,两个或多个线程需要共享对同一个数据访问。如果每个线程都会修改同一个数据,那么这些线程将会相互影响对方的运行。
为了避免多个线程同时访问一个共享数据,保证线程的方法在执行我完毕前不会被另一个线程打断,这种运行机制叫线程同步。
线程同步有两种方式:
1) 同步方法:
将Synchronized关键字放在方法的声明中,表示整个方法为同步方法;
2) 同步代码块
把线程体内执行的方法中会操作到共享数据的语句封装在{ }内,然后用Synchronized关键字放在前面修饰这个代码块。
格式如下:
Synchronized{
}