一、进程和线程
什么是进程?说简单一点,就是正在运行中的程序。那什么是程序呢?学术上对程序的定义是一组计算机能识别和执行的指令集合(《C程序设计》谭浩强著)。翻译成白话文,程序就是能在计算机上运行操作完成特定任务的源代码。只是程序是静态的,不会动的,无论何时程序都是存在的,当程序遇上数据和进程控制块之后,它就会摇身一变成为进程。进程的执行是并发的(不是并行的),每个进程之间相互独立,各个进程拥有独立的资源。并发执行是指在一个特定的时刻只能有一个进程在执行,我们之所以感觉到电脑可以执行“同时”执行很多进程那是因为每个进程在操作系统中被快速的轮换执行,微观上的快速交换使得宏观上表现的是“同时”执行,其实这只是一种假象而已。我们不妨来举个例子,一个人一边看电影,一边听演员的台词,看起来听和看是同时进行的,但事实情况真的是这样吗?人类的大脑在一个时刻只能处理看或者听的感知,当大脑在处理视觉时,听觉会被暂时搁置,当大脑处理听觉时,视觉处理会被暂时搁置,当两者被快速轮换时,我们感觉是同时看到画面和听到台词的。讲到这里,并发的好处就显而易见了,。假如让你先看一遍无声画面,再听一遍无画面台词,估计观众怎么也不会同意。
进程的出现解决的是CPU利用率的问题。进程属于在处理器这一层上提供的抽象;线程则属于在进程这个层次上再提供了一层并发的抽象。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
引入线程的好处:
(1)易于调度。
(2)提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。
(3)开销少。创建线程比创建进程要快,所需开销很少。
(4)利于充分发挥多处理器的功能。通过创建多线程进程(即一个进程可具有两个或更多个线程),每个线程在一个处 理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分运行。
二、实现线程的三种方式
(1)直接继承Thread类
public class FirstThreadTest extends Thread{
private static int i = 0;
public void run(){
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
public static void main(String[] args){
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
if(i==20){
new FirstThreadTest().start();
new FirstThreadTest().start();
}
}
}
}
(2)实现Runnable接口
public class RunnableTest implements Runnable{
private int i;
public void run(){
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+"----->"+i);
}
}
public static void main(String[] args){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"----->"+i);
if(i==20){
RunnableTest rt = new RunnableTest();
new Thread(rt,"新线程1").start();
new Thread(rt,"新线程2").start();
}
}
}
}
(3)使用Future和Callable接口创建线程
import java.util.concurrent.*;
public class FeatureAndCallableTest implements Callable<Integer>{
public Integer call(){
int i=0;
for(;i<100;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
return 50;
}
public static void main(String[] args){
FeatureAndCallableTest rt = new FeatureAndCallableTest();
FutureTask<Integer> task = new FutureTask<Integer>(rt);
for(int i = 0;i<100; i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
if(i == 20){
new Thread(task,"有返回值的线程").start();
}
}
try{
System.out.println("子线程的返回值"+task.get());
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
三、三种线程实现方式的比较
(1)由于Java中的单继承机制,当我们通过继承Thread类来启动线程时就不能爱继承其他类,但通过实现Runnble接口还可以继承其他类。
(2)继承Thread类启动多个线程,线程之间的资源不共享,而通过实现Runnable接口则可以实现资源共享
(3)继承Thread类实现线程的编程对于实现Runnable接口要简单
(4)Callable接口是Runnable接口的升级版,只是Callable接口中的方法有返回值并可抛出异常而已。