一、线程与进程
1.线程与进程的定义
进程:一个操作系统中会可以同时运行多个程序,一个程序就是一个进程。
线程:一个程序会并发性的执行多个任务,多个任务组成一个顺序执行流。
并发:区别于并行性,并行性是指同一时刻有多个任务执行;并发指的是在一段时间内一系列的任务轮番执行,但在宏观上是在一个时刻同时执行的。
多线程优势:
- 进程之间不能共享内存,但线程之间共享内存非常容易。
- 系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小很多,因此使用多线程来实现多任务并发比多进程高效的多。
- Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。
二、线程的创建与启动
1. 继承Thread类创建线程类
public class FirstThread extends Thread{
private int i;
//重写run()方法,run()方法的方法体就是线程执行体
public void run() {
for( ; i < 100 ; i++) {
//当线程类继承Thread类时,直接使用this即可获取当前线程
//Thread对象的getName()方法返回当前线程的名字
//因此可以直接调用getName()方法返回当前线程的名字
System.out.println(getName() + " " + i);
/*
* 程序可以通过setName(Stirng Name)方法为线程设置名字
* 使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量
*/
}
}
public static void main(String[] args) {
for(int i = 0 ; i < 100 ; i++) {
//调用Thread的currentThread()方法获取当前线程
System.out.println(Thread.currentThread().getName()
+ " " + i);
if(i == 20) {
//创建并启动第一个线程
new FirstThread().start();
//创建并启动第二个线程
new FirstThread().start();
}
}
}
}
尽管这段代码只是显示的创建并启动了两个线程,但其实在运行的一共三个线程(还有一个主线程); main()方法的方法体就是主线程的线程执行体
2. 实现Runnable接口创建线程类
//通过实现Runnable接口来创建并启动多线程
public class SecondThread implements Runnable {
private int i;
//run()方法同样是线程执行体
@Override
public void run() {
// TODO Auto-generated method stub
for( ; i < 100 ; i++) {
//当线程类实现Runnable接口时
//如果想获取当前线程,只能用Thread.currentThread()方法
//这一点上相比较,继承Thread类的做法会更容易,它只需要在this上进行引用即可
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) {
SecondThread st = new SecondThread();
//通过new Thread(target, name) 方法创建新线程
new Thread(st, "新线程1").start();
new Thread(st, "新线程2").start();
}
}
}
}
继承Thread类的对象可代表线程对象;Runnable接口创建的对象只是线程的target,而多个线程可共享一个线程类的实例变量
3. 使用Callable和Future创建线程
public class ThirdThread{
public static void main(String[] args) {
//创建Callable对象
ThirdThread rt = new ThirdThread();
//先使用Lambda表达式创建Callalbe<Integer>对象
//使用FutureTask来包装Callable对象
FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
int i = 0;
for( ; i < 100 ; i++) {
System.out.println(Thread.currentThread().getName()
+ "的循环变量i的值" + i);
}
//call()方法可以有返回值
return i;
});
for(int i = 0 ; i < 100 ; i++) {
System.out.println(Thread.currentThread().getName()
+ "的循环变量i的值" + i);
if(i == 20) {
//实质还是以Callable对象来创建并启动线程的
new Thread(task, "有返回值的线程").start();
}
}
try {
//获取线程返回值
System.out.println("子线程的返回值" + task.get());
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
Callable接口是Runnable接口的增强版;Callable接口提供了一个call()的方法,其可以作为线程执行体,其拥有如下特性
- call()方法可以有返回值;
- call()方法可以声明抛出异常
Java 5 提供了Future接口来代表Callable接口里的call()方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现了Future接口,并且实现了Runnable接口---可以作为Thread类的target。
Callable接口有泛型限制,其接口里的泛型参数与call()方法返回值类型相同,而且Callalbe接口是函数式接口,因此可以用Lambda表达式创建Callalbe对象。
Future接口提供了几种公共方法来控制它关联的Callalbe任务:
方法 | 功能 |
boolean cancel(boolean mayInterruptIfRunning) | 取消该Future里关联的Callable任务 |
V get() | 返回Callable任务里的call()方法的返回值 |
V get(long timeout, TimeUnit unit) | 返回Callable任务里的call()方法的返回值 |
boolean isCancelled() | 如果在Callable任务正常完成前被取消,则返回true |
boolean isDone() | 如果Callable任务已完成,则返回true |
- V get()方法将导致程序阻塞,必须等到子线程结束后才会得到返回值