在java中,创建线程,就是创建一个Thread对象

线程的内存模型

进程A 和 进程B 的内存独立不共享。
线程A 和 线程B 在Java中,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈。
image.png

继承Thread类,重写run方法

  1. class StartThread01 extends Thread {
  2. @Override
  3. public void run() {
  4. for (int i = 0; i < 200; i++) {
  5. System.out.println("我在学习i=" + i);
  6. }
  7. }
  8. }
  9. public static void main(String[] args) throws InterruptedException {
  10. StartThread01 p = new StartThread01();
  11. p.start();
  12. System.out.println(Thread.currentThread().getName());
  13. Thread.sleep(100);
  14. for (int i = 0; i < 100; i++) {
  15. System.out.println("主线程调用i=======" + i);
  16. }
  17. }

实现Runnable接口,重写run方法

new Thread(new MyRunnable).start()
底层就是静态代理的原理,Thread是代理类,代理目标是Runnable接口的实现类。start是代理类去调用的,Runnable接口的实现类并不包含start方法。

将Runnable实现类,传入Thread类的构造方法中。

  1. // 这只是一个可运行的类(Runnable),还不是一个线程
  2. class MyRunnable implements Runnable {
  3. public void run() {}
  4. }
  5. // 创建线程对象
  6. Thread t = new Thread(new MyRunnable);
  7. // 启动线程
  8. t.start();
  9. // 采用匿名内部类的形式
  10. Thread t = new Thread(new Runnable(){
  11. public void run() {
  12. }
  13. });

实现Callable接口,实现call方法

可以返回运行结果或抛出异常
FutureTask其实也是实现的Runnable接口

FutureTask(Callable[**V**](https://www.matools.com/file/manual/jdk_api_1.8_google/java/util/concurrent/FutureTask.html) callable)
创建一个 FutureTask ,它将在运行时执行给定的 Callable 。
FutureTask(Runnable runnable, V result)
创建一个 FutureTask ,将在运行时执行给定的 Runnable ,并安排 get将在成功完成后返回给定的结果。

可以传Callable 或者 Runable ,传入Runnable将没有返回值

方法

V get()
等待计算完成,然后检索其结果。

会导致当前线程阻塞,因为必须等待其他线程运行结束得到运行结果。

  1. 此时使用FutureTask类接收Callable接口,然后将FutureTask类传入Thread类,再通过Thread类启动线程。
  2. 使用线程池的submit接收Callable实现类,同时自动启动,然后通过Future<>接收结果 ```java import java.util.concurrent.FutureTask; // jdk8新增的包

public static void main(String[] args) {

  1. // 第一步:创建一个未来任务类对象(此处使用匿名类)
  2. FutureTask task = new FutureTask(new Callable<>(){
  3. // call() 相当于之前的run方法,只是此处有返回值
  4. public Object call() throws Exception {
  5. ...
  6. return new Object();
  7. }
  8. });
  9. // 更改--使用线程池
  10. ExecutorService executor = Executors.newCachedThreadPool();
  11. executor.submit(task);
  12. System.out.println(task.get());
  13. //

}

```java
public class ThreadBase03 implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " 执行:" + i);
        }
        return true;
    }

    public static void main(String[] args) throws Exception {

        ThreadBase03 thread01 = new ThreadBase03();
        ThreadBase03 thread02 = new ThreadBase03();
        ThreadBase03 thread03 = new ThreadBase03();

        // 创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        Future<Boolean> r1 = executorService.submit(thread01);
        Future<Boolean> r2 = executorService.submit(thread02);
        Boolean r3 = executorService.submit(thread03).get();

        Boolean rs1 = r1.get();
        Boolean rs2 = r2.get();
//        Boolean rs3 = r3.get();
        // 必须关闭线程池
        executorService.shutdown();

    }
}

第一个例子和第二个例子的区别是,第一个例子调用submit方法没有返回值,实际上调用的是submit(Runnable task)方法,而第二个例子调用的是submit(Callable<T> task)方法。而且第一个例子使用FutureTask去取值,第二个例子使用submit返回的future去取值。

在很多高并发的环境下,有可能Callable和FutureTask会创建多次。FutureTask能够在高并发环境下确保任务只执行一次。有兴趣的可以参看FutureTask源码。