文章目录
- 进程( Process )
- 线程(Thread)
- 线程的三种创建方法
- 一.继承Thread类创建线程类
- 二.实现Runnable接口创建线程类
- 三.使用Callable和Future创建线程(了解即可)
进程( Process )
- 我们都知道程序是指令和数据的有序集合,它是一个静态的概念,而进程就是处于运行过程中的程序,它有自己的生命周期和各种不同的状态;并且它拥有自己独立的资源和私有的地址空间。进程是系统进行资源分配和调度的一个独立单位。
- 当前的操作系统都支持同时运行多个任务,例如我们可以一边敲代码,一边听歌,一边回微信等等,每个独立运行的任务就是一个进程。这些进程看起来像是在同时工作,事实上并不是这样,对于一个CPU而言,它在某个时间点只能执行一个程序,也就是说只能运行一个进程,cpu不断在这些进程之间轮换执行,但是因为cpu的执行速度特别快,会给我们一种多个进程同时执行的错觉。这就是进程的并发性。
线程(Thread)
- 线程是进程的组成部分,一个进程可以拥有多个线程。
- 线程是cpu调度和执行的单位。
- 线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,但不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。
- 线程是独立运行的,它并不知道进程中是否还有其他线程存在。线程的执行是抢占式的,当前运行的线程随时都可能被挂起,以便另外一个线程可以运行。线程的运行是由调度器控制的,人为不可干预。
- 一个进程至少包含一个线程。在进程被初始化后,主线程(main())就被创建了,通常仅要求有一个主线程,作为系统的入口。除主线程,后台还有gc()线程等。
线程的三种创建方法
一.继承Thread类创建线程类
- 定义Thread的子类。
- 重写Thread的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建线程对象。
- 调用线程对象的start()方法来启动该线程
- 话不多说,上代码:
//通过继承Thread类来创建线程类
public class MyThread extends Thread {
//重写run()方法
@Override
public void run() {
for (int i = 0; i < 20; i++) {
//当线程类继承Thread类时,使用This可获得当前线程
//getName()是Thread类的实例方法,该方法返回调用该方法的线程名字
//通过Thread对象的getName()方法返回当前线程的名字
System.out.println(this.getName()+" "+i);
}
}
//主线程
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
//currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象
System.out.println(Thread.currentThread().getName()+" "+i);
if(i==7){
//创建并启动一个线程
new MyThread().start();
//再次创建并启动一个线程
new MyThread().start();
}
}
}
}
//运行结果如下(每次的运行结果都有可能不一样,因为cpu安排调度,所以线程不一定立即执行)
main 0
main 1
main 2
main 3
main 4
main 5
main 6
main 7
main 8
main 9
main 10
main 11
main 12
main 13
Thread-1 0
Thread-1 1
Thread-1 2
Thread-1 3
Thread-1 4
Thread-1 5
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
Thread-1 10
Thread-1 11
Thread-1 12
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-1 13
main 14
Thread-1 14
Thread-1 15
Thread-1 16
Thread-1 17
Thread-1 18
Thread-1 19
Thread-0 9
Thread-0 10
Thread-0 11
Thread-0 12
Thread-0 13
Thread-0 14
Thread-0 15
Thread-0 16
Thread-0 17
Thread-0 18
Thread-0 19
main 15
main 16
main 17
main 18
main 19
二.实现Runnable接口创建线程类
- 定义Runnable接口的实现类MyRunnable。
- 实现run()方法,编写线程执行体
- 创建实现类MyRunnable的实例,将此实例作为Thread的target创建Thread对象,然后调用start()方法启动线程
- 话不多说,看代码
//通过实现Runnable接口来实现线程类
public class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//线程类实现Runnable接口时,若想获取当前线程,只能用Thread.currentThread().getName()
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==70){
//创建实现类MyRunnable的实例
MyRunnable myRunnable = new MyRunnable();
//通过new Thread(target,name)方法创建新线程
//将实例myRunnable作为Thread的target,然后调用start()方法启动线程
new Thread(myRunnable,"线程一").start();
new Thread(myRunnable,"线程二").start();
}
}
}
}
//运行结果 略
三.使用Callable和Future创建线程(了解即可)
- Callable接口像是Runnable接口的增强版,Callable接口提供了一个call()方法来作为线程执行体,但call()方法比run()方法更强大。
- call()方法可有有返回值
- call()方法可以声明抛出异常
- 因此完全可以提供一个 Callable对象作为Thread的target,但是因为Callable接口不是Runnable接口的子接口,所以Callable对象不能直接作为Thread的target。
- java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该类实现了Future接口和Runnable接口,可以作为Thread类的Target。
- 代码如下(还有其他方法)
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class AnotherThread {
public static void main(String[] args) {
AnotherThread at = new AnotherThread();
//使用Lambda表达式创建Callable<Integer>对象,并用FutureTask来包装此对象
FutureTask<Integer> task = new FutureTask<>((Callable<Integer>) () -> {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
});
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if(i==70){
new Thread(task).start();
}
}
}
}
- 前两种方式对比
- 采用方式一:
- 编写简单,但是继承了Thread,不能继承其他父类。不建议使用,避免oop单继承局限性
- 采用方式二:
- 编程稍复杂,但是避免了单继承局限性,多个线程可以共享一个target,方便多个线程来处理同一个对象,灵活方便。因而推荐使用第二种