一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。多线程是多任务的一种特别形式,但多线程使用了更小的资源开销。
一个进程包括有操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在。它必须是进程的一部分。一个进程一直运行直到所有的非守护线程都结束运行后才能结束。
进程 | 线程 | |
---|---|---|
定义 | 进程是处于运行中的程序,并且具有一定的独立功能。进程是系统进行资源分配和调度的一个单位。当程序进入内容时,即为进程。 | 线程是进程的组成部分,一个进程可以拥有多个线程,而一个线程必须拥有一个父进程。线程可以拥有自己的堆栈,自己的程序计数器和局部变量,但不能拥有系统资源。它与父进程的其他线程共享该进程的所有资源。 |
特点 | 1. 独立性:进程是系统中独立存在的实体,它可以独立拥有资源,每一个进程都有自己独立的地址空间,没有进程本身的运行,用户进程不可以直接访问其他进程的地址空间。2. 动态性:进程与程序的区别在于进程是动态的,进程中有时间的概念,进程具有自己的生命周期和各种不同的状态。3. 并发性:多个进程可以在单个处理器上并发执行,互不影响。 | 1. 线程可以完成一定任务,可以和其他线程共享父进程的共享变量和部分环境,互相协作来完成任务。2. 线程是独立运行的,其不知道进程中是否还有其他线程存在。3. 线程的执行时是抢占式,也就是说当前执行的线程随时可能被挂起,一边运行另一个线程。4. 一个线程可以创建或撤销另一个线程,一个进程中的多个线程可以并发执行。 |
一个线程对象在它的生命周期内,需要经历五种状态:
Thread 类实现了 Runnable 接口,在 Thread 类中,有一些比较关键的属性,比如:
由于使用 Thread 需要继承该类,限制了程序的扩展,一般不推荐使用 Thread。
如果一个类继承 Thread,则不适合资源共享。但是如果实现了 Runnable 接口的话,则很容易的实现资源共享。
实现 Runnable 接口比继承 Thread 类具有以下优势:
一般推荐使用 Runnable。
下面通过示例更好的理解Thread和Runnable,借鉴网上一个比较具有说服性的例子:3个人一共卖10张门票。
Java
class MyThread extends Thread {
private int ticket = 10;
private int count = 20;
@Override
public void run() {
for (int i = 0; i < count; i++) {
if (this.ticket > 0) {
System.out.println(this.getName() + " 卖票:" + this.ticket--);
}
}
}
}
启动线程
Java
public static void main(String[] args) {
// 启动三个线程,t1,t2,t3,每个线程各卖10张票
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
运行结果
Code
Thread-0 卖票:10
Thread-0 卖票:9
Thread-0 卖票:8
Thread-0 卖票:7
Thread-0 卖票:6
Thread-0 卖票:5
Thread-0 卖票:4
Thread-0 卖票:3
Thread-0 卖票:2
Thread-0 卖票:1
Thread-1 卖票:10
Thread-1 卖票:9
Thread-1 卖票:8
Thread-1 卖票:7
Thread-1 卖票:6
Thread-1 卖票:5
Thread-1 卖票:4
Thread-2 卖票:10
Thread-1 卖票:3
Thread-2 卖票:9
Thread-1 卖票:2
Thread-2 卖票:8
Thread-1 卖票:1
Thread-2 卖票:7
Thread-2 卖票:6
Thread-2 卖票:5
Thread-2 卖票:4
Thread-2 卖票:3
Thread-2 卖票:2
Thread-2 卖票:1
说明
主线程 main 创建并启动 3个 MyThread 子线程,每个子线程都各自卖出了10张门票。
Java
class MyRunnable implements Runnable {
private int ticket = 10;
private int count = 20;
public void run() {
for (int i = 0; i < count; i++) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName() + " 卖票:" + this.ticket--);
}
}
}
}
启动线程
Java
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
// 启动三个线程,t1,t2,t3(它们共用一个Runnable对象),这三个线程一共卖10张票
Runnable target;
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
Thread t3 = new Thread(myRunnable);
t1.start();
t2.start();
t3.start();
}
运行结果
Code
Thread-0 卖票:10
Thread-0 卖票:9
Thread-0 卖票:8
Thread-0 卖票:7
Thread-0 卖票:6
Thread-1 卖票:5
Thread-0 卖票:4
Thread-0 卖票:2
Thread-0 卖票:1
Thread-1 卖票:3
---- 但会存在以下情况 ----
Thread-0 卖票:10
Thread-0 卖票:9
Thread-0 卖票:8
Thread-0 卖票:7
Thread-0 卖票:6
Thread-0 卖票:5
Thread-0 卖票:4
Thread-0 卖票:3
Thread-0 卖票:2
Thread-0 卖票:1
Thread-2 卖票:0
Thread-1 卖票:10
说明
主线程main创建并启动3个子线程,而且这3个子线程都是基于“myRunnable这个Runnable对象”而创建的。运行结果是这3个子线程一共卖出了10张票。这说明它们是共享了MyRunnable接口的。