标签:
Windows等操作系统均支持多线程进程的并发处理机制。操作系统支持多线程,使多个程序能够并发执行,以改善资源使用率和提高系统效率;操作系统支持多线程,能够减少程序并发时所付出的时间和空间开销,使得开发粒度更细,并发性更好。
进程是一个程序关于某个数据集合的一次执行过程,是操作系统进行资源分配和保护的基本单位。进程具有以下特性:
①结构性。进程包含了数据集合和运行于其上的程序。每个进程至少由三要素组成:程序块、数据块和进程控制块。进程控制块(Process Control Block, PCB)描述和记录进程的动态变化过程,使进程能正确运行。
②独立性。进程既是系统中资源分配和保护的基本单位,也是系统调度的独立单位(单线程进程),每个进程都以各自独立的速度在CPU上运行。
③动态性。进程是程序在数据集合上的一次执行过程,是动态概念。它的生命周期在多个状态间变化,由创建而产生,由调度而执行,因等待条件而阻塞,由撤销而消亡。程序是一组有序指令序列,是静态概念,程序作为一种系统资源是永久存在的。
④并发性。进程的并发性是指一组进程的执行在时间上是重叠的。
⑤交互性。多个进程可以共享变量,通过共享变量实现互相通信,多个进程之间能够协作完成一个任务。
线程是进程中能够独立执行的实体(控制流),是处理器调度和分配的基本单位。线程是进程的组成部分,每个进程内允许包含多个并发执行的线程。同一个进程中的所有线程共享进程获得的内存空间和资源,但不拥有资源。
支持多线程的进程成为多线程进程。
线程的主要特性如下:
①结构性。线程是操作系统调度的基本单位,具有唯一的标识符合线程控制块,其中包含调度所需的一切信息。
②动态性。线程是动态的,而且有状态变化。当创建一个进程时,同时至少为其创建一个线程,需要时再创建其他线程。终止一个进程将导致进程中的所有线程终止。
③并发性。同一进程的多个线程可在一个或多个处理器上并发或并行的执行,进程之间的并发执行演变为线程之间的并发执行。在单处理器上,从宏观上看,在一个时间段中有几个线程都处于运行状态;在微观上看,任意时刻仅有一个线程在处理器上运行。并发的实质是一个处理器在多个线程之间的多路复用,是对有限的物理资源强制行使多用户共享,消除计算机不见之间的互等现象,提高系统资源利用率。
④共享性。同一进程的所有线程共享但部拥有进程的状态和资源,且驻留在进程的内存空间中,可以访问相同的数据。所有线程之间需要有通信和同步机制。
线程在其生命周期中经历着状态的变化,线程状态包括5种:新建、就绪、运行、阻塞、终止。
就绪(ready)态——进程具备运行条件,等待系统分配处理器以便运行。
运行(running)态——进场占用处理器正在运行。
阻塞(blocked)态——进程不具备运行条件,正在等待某个事件的完成。
线程在执行过程中的任一时刻,处于一种状态,根据运行条件在多个状态之间转变。一个进程创建后处于就绪态,运行中因等待条件处于阻塞态。
任一时刻只有一个线程能够占用一个处理器运行,按照什么原则决定就绪队列中的哪个线程能够获得处理器就是线程调度的任务。
线程调度的功能就是按照某种原则选择一个线程使它获得处理器运行。线程调度是操作系统的核心部分,线程调度策略的优劣直接影响到操作系统的性能。
线程调度采用剥夺方式,当一个线程正在处理器上执行时,操作系统可以根据规定的原则剥夺它的处理器使用权,而把处理器分配给其他线程使用。常用的剥夺原则有两种:一是高优先级线程可以剥夺低优先级线程运行;二是当运行线程时间使用完后被菠萝处理器。
顺序程序设计方法是指,程序模块按照语句次序顺序执行,其特性为:①执行的顺序性;②运行环境的封闭性;③执行结果的确定性;④计算解雇偶的可再现性。
并发程序设计方法是指,将一个程序分为若干可同时执行的程序模块,每个程序模块和它执行时所处理的数据结合组成一个进程。操作系统以进程作为系统资源分配的基本单位,以线程系统调度的基本单位。其特性如下:
①并发执行的线程之间不具有顺序性。线程由操作系统调度执行,不会按照语句的书写顺序执行。
②运行环境不再是封闭的,一个线程的执行可能影响其他线程的执行结果。(计算过程不可再现)
③共享变量的多个线程(成为交互线程)之间实现线程通信,能够协作完成一个任务,也会出现与时间有关的错误。
④并发多线程程序设计的优点是,提高了系统性能,具体表现为快速切换线程、减少系统管理开销、线程通信易于实现、并发按程序提高、节省内存空间。
Java支持内置的多线程机制。Java语言包中的Runnable接口约定线程的执行方法,Thread类提供创建、管理和控制线程对象的方法。
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
public class NumberThread extends Thread{
private int first;
public NumberThread(String name, int first){
super(name);
this.first = first;
}
public void run(){
System.out.print(this.getName() + ":");
for (int i = first; i < 100; i += 2){
System.out.print(i + " ");
}
System.out.println(this.getName() + "End!\n");
}
public static void main(String[] args){
System.out.println("Current Thread:" +Thread.currentThread().getName());
NumberThread thread1 = new NumberThread("JISHU", 1);
NumberThread thread2 = new NumberThread("OUSHU", 2);
thread1.start();
thread2.start();
System.out.println("Active Count:" + Thread.activeCount());
}
}
在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)。
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源)
public class NumberRunnable implements Runnable{
private int first;
public NumberRunnable(int first){
this.first = first;
}
public void run(){
for (int i = first; i < 50; i += 2){
System.out.print(i + " ");
}
System.out.println("End!\n");
}
public static void main(String[] args){
NumberRunnable target = new NumberRunnable(1);
Thread thread1 = new Thread(target, "JISHU");
thread1.start();
new Thread(new NumberRunnable(2), "OUSHU").start();
}
}
两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
①适合多个相同的程序代码的线程去处理同一个资源(适合于资源共享)
②可以避免java中的单继承的限制
③增加程序的健壮性,代码可以被多个线程共享,代码和数据独立。
用一个买票程序来说明两种实现方式的区别:
继承Thread类:
public class TicketThread extends Thread {
private int count = 10;
public TicketThread(String name){
super(name);
}
public void run(){
while (count > 0){
System.out.println(Thread.currentThread().getName() + " is selling ticket: " + count);
count--;
}
}
public static void main(String[] args){
TicketThread thread1 = new TicketThread("窗口1");
TicketThread thread2 = new TicketThread("窗口2");
thread1.start();
thread2.start();
}
}
输出结果为:
窗口2 is selling ticket: 10
窗口2 is selling ticket: 9
窗口2 is selling ticket: 8
窗口2 is selling ticket: 7
窗口2 is selling ticket: 6
窗口2 is selling ticket: 5
窗口2 is selling ticket: 4
窗口2 is selling ticket: 3
窗口2 is selling ticket: 2
窗口2 is selling ticket: 1
窗口1 is selling ticket: 10
窗口1 is selling ticket: 9
窗口1 is selling ticket: 8
窗口1 is selling ticket: 7
窗口1 is selling ticket: 6
窗口1 is selling ticket: 5
窗口1 is selling ticket: 4
窗口1 is selling ticket: 3
窗口1 is selling ticket: 2
窗口1 is selling ticket: 1
实现Runnable接口:
public class TicketRunnable implements Runnable{
private int count = 10;
public void run(){
while (count > 0){
System.out.println(Thread.currentThread().getName() + " is selling ticket: " + count--);
}
}
public static void main(String[] args){
TicketRunnable target = new TicketRunnable();
new Thread(target, "窗口1").start();
new Thread(target, "窗口2").start();
}
}
输出结果为:
窗口1 is selling ticket: 9
窗口1 is selling ticket: 8
窗口1 is selling ticket: 7
窗口1 is selling ticket: 6
窗口1 is selling ticket: 5
窗口1 is selling ticket: 4
窗口1 is selling ticket: 3
窗口1 is selling ticket: 2
窗口1 is selling ticket: 1
窗口2 is selling ticket: 10
如果多次执行,可以发现上述程序可能出现卖出编号为0的票的情况,这里涉及线程的同步机制,将在后续文章中提到。
标签:
原文地址:http://blog.csdn.net/foreverling/article/details/46572621