1. 进程和线程
进程是运行着的程序,是程序在操作系统下的一次执行过程,是一个程序的动态概念。进程是操作系统分配资源的基本单位
线程是进程中的一个执行单元,一个进程可以包含多个线程。线程共享进程的资源(如内存、文件句柄),但有各自的栈空间和寄存器。线程是CPU执行的基本单位。
从Java程序理解:
- 启动一个java程序,操作系统就会创建一个java进程
- 在一个线程里可以创建多个线程,所以在一个java程序里,可以自定义创建多个线程,这些线程拥有各自独立的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。
2. 创建线程
创建线程有三种方法:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
Thread
继承Thread类并重写run()方法。
public class MyThread1 extends Thread {
@Override
public void run() {
System.out.println("继承Thread类实现线程");
}
public static void main(String[] args){
MyThread1 myThread1 = new MyThread1();
myThread1.start();
}
}
Runnable
实现 Runnable 接口优于继承 Thread 类,因为:
- Java 不支持多重继承,所有的类都只允许继承一个父类,但可以实现多个接口。如果继承了
Thread类就无法继承其它类,这不利于扩展。 - 类可能只要求可执行就行,继承整个
Thread类开销过大。
public class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"实现Runnable接口实现线程");
}
public static void main(String[] args) {
Thread thread=new Thread(new MyThread2(),
"线程1");
thread.start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"匿名内部类实现Runnable接口");
},"线程2").start();
}
}
Callable
继承 Thread 类和实现 Runnable 接口这两种创建线程的方式都没有返回值。所以,线程执行完后,无法得到执行结果。但如果期望得到执行结果该怎么做?
为了解决这个问题,Java 1.5 后,提供了 Callable 接口和 Future 接口,通过它们,可以在线程执行结束后,返回执行结果。
Callable 接口只声明了一个方法,这个方法叫做 call():
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Future 就是对于具体的 Callable 任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过 get 方法获取执行结果,该方法会阻塞直到任务返回结果。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask 类实现了 RunnableFuture 接口,RunnableFuture 继承了 Runnable 接口和 Future 接口。所以,FutureTask 既可以作为 Runnable 被线程执行,又可以作为 Future 得到 Callable 的返回值。
public class FutureTask<V> implements RunnableFuture<V> {
// ...
public FutureTask(Callable<V> callable) {}
public FutureTask(Runnable runnable, V result) {}
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
事实上,FutureTask 是 Future 接口的一个唯一实现类。
#Callable + Future + FutureTask 示例
通过实现 Callable 接口创建线程的步骤:
- 创建
Callable接口的实现类,并实现call方法。该call方法将作为线程执行体,并且有返回值。 - 创建
Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值。 - 使用
FutureTask对象作为Thread对象的 target 创建并启动新线程。 - 调用
FutureTask对象的get方法来获得线程执行结束后的返回值。
public class MyThread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 123;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> ft1 = new FutureTask<>(new MyThread3());
new Thread(ft1, "实现Callable接口的线程").start();
System.out.println(ft1.get());
FutureTask<String> ft2 = new FutureTask<>(() -> "匿名内部类实现callable接口");
new Thread(ft2).start();
System.out.println(ft2.get());
}
}
3. 线程常用方法
| 方法 | 描述 |
|---|---|
| run | 线程的执行实体 |
| start | 线程的启动方法 |
| Thread.currentThread | 返回当前线程的引用 |
| setName | 设置线程名称 |
| getName | 获取线程名称 |
| setPriority | 设置线程优先级,范围[1,10],一般说,高优先级的线程在运行时具有优先权。默认优先级为5 |
| setDaemon | 设置线程为守护线程 |
| isDaemon | 判断线程是否为守护线程 |
| isAlive | 判断线程是否启动 |
| interrupt | 中断另一个线程的运行状态,并不是立即中断,而是设置线程的中断状态为 true(默认是 flase) |
| isInterrupted | 测试当前线程是否被中断 |
| Thread.interrupted | 检测当前线程是否被中断,与 isInterrupted() 方法不同的是,这个方法如果发现当前线程被中断,会清除线程的中断状态 |
| join | 使一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待该线程执行完才继续执行 |
| Thread.sleep | 静态方法。将当前正在执行的线程休眠 |
| Thread.yield | 静态方法。将当前正在执行的线程暂停,让其他线程执行 |