一、线程与进程的区别
- 一个程序至少有一个进程,一个进程至少有一个线程。
- 进程是一个实体。每一个进程都有它自己的地址空间和资源,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。
- 在一个程序中,这些独立运行的程序片段叫作“线程”(Thread)。
- 线程可以利用进程所拥有的资源。
- 通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
二、多线程
多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。
三、Java中多线程的创建及启动
- 继承Thread类,重写该类的run()方法,使用start开始执行。
class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
使用方法:
Thread myThread1 = new MyThread();
myThread1.start();
- 实现Runnable接口,并重写该接口的run()方法。然后以此实例作为Thread类的target来创建Thread对象,并执行。
class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
使用方法:
Runnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
thread1.start();
- 使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
class MyCallable implements Callable<Integer> {
private int i = 0;
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
使用方法:
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
thread.start(); //线程进入到就绪状态
FutureTask类实际上是同时实现了Runnable和Future接口,由此才使得其具有Future和Runnable双重特性。通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。
Thread和Runnable之间关系
- Thread需要继承,而Runnable只要实现接口就可以。在Java中,只能继承一个类,但是可以实现多个接口。所以Runnable比Thread使用起来更方便。
- Runnable下更适合资源共享的情况。比如开多个线程一起来卖票,票的数量是固定的,那么多个线程共享这么多数量的票。
所以多线程的情况下,尽量使用Runnable。
这一节就讲到这里,在下一篇文章中会重点介绍线程安全。