一、概念
进程:
进程是资源(CPU,内存等)分配的基本单位,它是程序执行的一个实例。
程序运行时就会创建一个进程,并为他分配资源,然后将创建的进程放入进程就绪队列中。
进程调度器选中它的时候就会为它分配CPU时间,程序开始真正的运行。
线程:
线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位。
一个进程可以由多个线程组成,线程之间共享进程的所有资源,每个线程都有自己的堆栈和局部变量。
线程由CPU独立调度执行,多个线程同时执行就可以实现并发操作。
通俗解释:
比如一个取款的功能,那么这个功能就相当于一个进程,而多个同时取款,每个人都会有一个请求,而每个请求都会分配一个线程来处理。
二、线程的五个状态
新建 ------ 就绪 ------ 运行 -------- 阻塞 ----- 消亡
三、 线程的创建
1.继承Thread类并重写run方法
public class ThreadDemo1 {
public static class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("MyThread1 is running");
}
}
//使用
@Test
public void testOne() {
//继承Thread类写法
new MyThread1().start();
}
}
2.实现Runable接口
public class ThreadDemo1 {
public static class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println("MyThread2 is running");
}
}
@Test
public void testOne() {
//实现Runable接口写法
MyThread2 thread2 = new MyThread2();
new Thread(thread2).start();
//实现Runable接口写法,使用匿名内部类创建线程
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread3 is running");
}
}).start();
//实现Runable接口写法,使用java8新特性,函数式编程创建线程
new Thread(() ->{
System.out.println("thread4 is running");
}).start();
}
}
3.实现Callable接口
Callable一般是配合线程池工具ExecutorService来使用。
public class ThreadDemo1 {
public static class MyCallAble implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 2;
}
}
@Test
public void testTwo() throws ExecutionException, InterruptedException {
//创建ExecutorService实例
ExecutorService service = Executors.newCachedThreadPool();
MyCallAble callAble = new MyCallAble();
//通过ExecutorService的submit方法来执行线程
Future<Integer> future = service.submit(callAble);
System.out.println(future.get());
}
}
Callable方法创建的线程执行完毕后会返回一个Future接口的实例,可以通过该实例做一些线程的操作。
Future接口只有几个简单的方法。如下所示,通过方法名应该能知道什么意思,就不写注释了。
public abstract interface Future<V> {
public abstract boolean cancel(boolean paramBoolean);
public abstract boolean isCancelled();
public abstract boolean isDone();
public abstract V get() throws InterruptedException, ExecutionException;
public abstract V get(long paramLong, TimeUnit paramTimeUnit)
throws InterruptedException, ExecutionException, TimeoutException;
}
cancel方法是试图取消一个线程的执行。
注意是试图取消,并不一定能取消成功。存在取消失败的可能。boolean类型的返回值是“是否取消成功”的意思。参数paramBoolean表示是否采用中断的方式取消线程执行
四、相关类理解
1.FutureTask类
上面介绍了Future接口。这个接口有一个实现类叫FutureTask。FutureTask是实现的RunnableFuture接口的,而RunnableFuture接口同时继承了Runnable接口和Future接口:
RunnableFuture接口代码 如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
FutureTask类里面实现了Future接口中cancle,isDone,get等方法的具体内容。
FutrueTask类的使用方法如下:
public class ThreadDemo1 {
public static class TestFutureTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 2;
}
}
@Test
public void testThree() throws ExecutionException, InterruptedException {
ExecutorService service = Executors.newCachedThreadPool();
FutureTask<Integer> task = new FutureTask<>(new TestFutureTask());
service.submit(task);
System.out.println(task.get());
}
}
解释:
FutureTask在 使用上和前面的Future方法还是有点区别的,
首先获取返回值是通过FutureTask的实例去获取的,
启动线程是service.submit(task);也是有返回值的,返回一个Future实例,但是 通过Future实例去获取线程的返回值是null。如下图所示
注意:有文章说在很多高并发的情况下,
FutureTask task = new FutureTask<>(new TestFutureTask());这一句可能有问题
Callable的实现类TestFutureTask会被创建多个实例,FutureTask也可能会创建多个实例。
但是FutureTask能够在高并发的环境下确保线程的任务只执行依次。
这里我没有验证,有机会验证一下。