多线程编程的含义:将程序任务分成几个并行的子任务,特别是在网络编程中,很多功能都是要并发执行的。本节课将从线程的概念、开发方法及运行来讲解。
- 线程的概念:包括进程和线程的区别、线程的概念、运行状态和优先级
- 线程的开发方法:继承Thread和实现Runnable、定时器TimberTask以及线程组ThreadGroup和线程池
- 线程的运行:线程的启动、停止、休眠、挂起、同步机制
一、线程的概念
1、进程与线程的区别
a)进程。大多数操作系统中都可以创建多个进程。每当启动一个程序,它可以为即将开始的每项任务创建一个进程,并允许他们同时运行。当一个程序被阻塞时,另一个进程还可以运行,以增加资源利用率。每个进程都要占用相当一部分处理器时间和内存资源。进程之间通信不方便。
b)线程。线程也成为轻型进程。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程要廉价。线程允许协作与交换数据。线程需要操作系统的支持,不是所有的机器都提供线程。
2、线程的概念
线程是彼此相互独立的、能够独立运行的子任务,并且每个线程都有自己的调用栈。所有多任务是通过周期性地将CPU时间片切换到不同的子任务,虽然从微观上看来,单核的CPU上只运行一个子任务,但是从宏观来看,每个子任务似乎都在同时连续运行。
我们现在使用的大多操作系统都是属于多任务、分时操作系统。正是由于这种操作系统的出现才有了多线程的概念。
3、线程的运行状态
在Java中,多线程就是一个类或者一个程序执行或管理多个线程执行任务的能力,每个线程可以独立于其他线程而独立运行,当然也可以和其他线程协作运行,一个类控制着它的所有线程,可以决定哪个线程得到优先级,那个线程可以访问其他类的资源,哪个线程开始执行,哪个线程保持休眠。线程的运行机制如下:
线程的状态表示线程正在进行的活动以及在此时间段内所能完成的任务。线程有创建、可运行、运行中、阻塞和死亡5中状态。一个具有生命的线程,总是处于这5中状态中。
- 新建状态:使用new创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程处于创建状态
- 可运行状态:使用start()方法启动一个线程之后,系统为该线程分配了除CPU外的所需资源,是该线程处于可运行状态(Runnable)
- 运行状态:Java运行系统通过调度选中了一个Runnable线程,使其占有CPU并转化为运行中(Running),此时,系统真正执行线程的run()方法
- 阻塞状态:一个正在运行的线程因为某种原因不能继续进行,进入阻塞状态
- 死亡状态:线程结束
4、线程的优先级
从概念上说,线程是并发运行的,但是从计算机的运行角度来说,确是串行执行的。当系统只有一个CPU时,线程会以某种顺序执行,这称为线程调度。
Java采用的是一种简单、固定的调度法,即固定优先级调度。这种算法是根据处于可运行状态线程的相对优先级来实行调度。当线程产生时,它继承原线程的优先级。在需要时可对优先级进行修改。如果两个线程具有相同的优先级,他们将被交替运行。
Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程状态变为可执行状态,那么系统将选择高优先级线程来运行。
同一时刻如果有多个线程处于可运行状态,则他们需要排队等待CPU资源。此时每个线程自动按照获得线程的优先级,线程的优先级的高低反映线程的重要或仅仅成都。可运行状态的线程按照优先级排队,线程调度依据优先级基础上的“先到先服务”原则。
线程调度管理器负责线程排队和CPU在线程之间的分配,并由线程调度算法进行调度。当线程调度管理器选中某个线程时,该线程获得CPU资源而进入可运行状态。线程调度是先占式调度,即如果在当前线程执行过程中一个更高级的线程进入可运行状态,则这个线程立即被调度执行。先占式调服分为:独占式和分时占式。
独占式:当前执行线程将一直执行下去知道执行完毕或者某种原因主动放弃CPU,或者CPU被一个更高级的线程抢占。
分时占式:当前运行线程获得一个时间片,当时间到时,即使没有执行完也要让出CPU,进入可运行状态,等待下一个时间片的调度,系统选中其他可运行状态的线程执行。分时占式的系统使每个线程工作若干步,实现多线程同时运行。
二、线程的开发方法
线程所有的操作都发生在线程体中,在java中线程体是从Thread类继承的run方法或者实现Runnable接口的类中的run方法,当线程产生并初始化后,实时系统调用它的run方法。run方法内的代码实现线程的行为,它是线程的主要部分。
本小节将首先讲解Java程序中进行多进程调度的两种方法
使用Runtime类
使用ProcessBuilder类
然后再讲解在Java中实现一个线程的两种方法
实现Runnable接口实现它的run方法
继承Thread类,覆盖它的run方法
两种方式的区别,如果你已经继承了其他类,那么你只能选择实现Runnable接口了,因为java是单继承关系
1、使用继承调用java程序
有时我们为了系统稳定性的需要,我们在java 代码中启动多个Java子进程。我们可以使用两种方法来实现这种要求。最简单的就是通过Runtime和exec方法来执行Java类名。如果执行成功,则返回一个Process对象,如果执行失败,那么将抛出一个IOException错误。使用ProcessBuilder建立子进程,调用ProcessBuilder的start方法启动子进程,其余跟Runtime异常。
2、使用继承创建java线程
Thread是一个具体类,不是一个抽象类,它封装了线程的行为。
public class Thread extends Object implements Runable
线程是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。每个线程多有优先级,高优先级线程的执行优先。每个线程可以标记为一个守护线程。
例如:计算大于某一规定值的质数的线程
class PrimeThread extends Thread{
long minPrime;
PrimeThread(long minPrime){
this.minPrime = minPrime;
}
public void run(){
//compute primes larger than minPrime
}
}
PrimeThread p = new PrimeThread(143);
p.start();