并发编程(二)-创建线程的方式

文章目录

  • 一、创建线程
    • 1.方法一,直接使用Thread
    • 2.方法二,使用 Runnable 配合 Thread
    • 3. 方法三,使用 FutureTask 配合 Thread
    • 4. 方法四,线程池创建线程

一、创建线程

  • 每个java程序在启动时,都已经创建了一个线程,这个线程就是main线程。
  • 如果需要在main线程之外再创建线程,则可以通过多种方式创建线程。

1.方法一,直接使用Thread

// 创建线程对象
// 构造方法的参数是给线程指定名字
Thread t1 = new Thread("t1") {
            @Override
            // run 方法内实现了要执行的任务
            public void run() {
                log.debug("hello");
            }
        };
// 启动线程
t1.start();

// 使用lambda表达式简写
Thread t2 = new Thread(() -> {
            log.debug("running");
        }, "t2");
t2.start();

2.方法二,使用 Runnable 配合 Thread

把【线程】和【任务】(要执行的代码)分开

  • Thread 代表线程
  • Runnable 可运行的任务(线程要执行的代码)
public static void main(String[] args) {
        Runnable r = () -> {
            log.debug("running");
        };
        Thread t = new Thread(r, "t");
        t.start();
    }
  • 原理之 Thread 与 Runnable 之间的关系
  1. 继承 Thread 类创建线程,把线程和任务合并在一起了;实现Runnable接口,把线程和任务分开了。
  2. 用 Runnable 接口更容易与线程池等高级API配合,线程池只能放入实现 Runable 或 Callable 接口的线程,不能直接放入继承 Thread 的类。
  3. 用 Runnable 接口让任务类脱离 Thread 继承体系,使用更加灵活。
  4. Thread 和 Runnable 实际执行的内容是各自重写的run方法中的内容。

Runnable源码

@FunctionalInterface
public interface Runnable {
  // Runnable是函数式接口,只有一个抽象run方法
  public abstract void run();
}

Thread源码

// Thread实现了Runnable接口
public class Thread implements Runnable {
   // Thread 与 Runnable 之间是 组合关系
   // 合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
   private Runnable target;
  
   // 带有 Runnable 参数、线程名参数 的构造方法
   public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    
   private void init(...){
      ...
      // 在init方法中将Runnable对象赋值给了 Thread 的成员变量 target
      this.target = target;
    }
    
    // ...此处省略其余源码
    
    // 1.传入的Runnable对象会在Thread的run方法中用到
    @Override
    public void run() {
    // Runnable对象不为空,则调用Runnable对象重写的run方法
        if (target != null) {
            target.run();
        }
    }
}

3. 方法三,使用 FutureTask 配合 Thread

  • FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况。
public static void main(String[] args) throws ExecutionException, InterruptedException {

       // 创建任务对象
       FutureTask<Integer> futureTask = new FutureTask(() -> {
           log.debug("callable执行任务");
           Thread.sleep(1000);
           return 100;
       });

       // 参数1 是任务对象; 参数2 是线程名
       Thread t1 = new Thread(futureTask, "t1");
       t1.start();

       // 主线程阻塞,同步等待 futureTask 执行完毕的结果
       Integer result = futureTask.get();
       // 1秒后获取到结果
       log.debug("获取FutureTask结果:{}", result);
   }
22:36:52.884 c.FutureTaskThread [t1] - callable执行任务
22:36:53.899 c.FutureTaskThread [main] - 获取FutureTask结果:100

Process finished with exit code 0

Callable源码

// Callable是函数式接口,有返回结果,能抛出异常
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

FutureTask源码

// 1.FutureTask 能够接收 Callable 类型的参数,用来处理有返回结果的情况,get方法可以获取返回结果
// 2.FutureTask类是 Runnable接口 的扩展,FutureTask 既有Runnable中的run方法,又有Future中的get方法
public class FutureTask<V> implements RunnableFuture<V> {
  public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
}

// 接口可以多继承
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

4. 方法四,线程池创建线程

public static void main(String[] args) {
	// 自定义线程池参数
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2, // 核心线程数
        5, // 最大线程数
        10, // 非核心线程的存活时间
        TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(10),
        new AbortPolicy());

    for (int i = 0; i < 20; i++) {
        int finalI = i;
        executor.execute(() -> {
            System.out.println(Thread.currentThread().getName() + " i:" + finalI);
        });
    }
}
  • Runnable和Callable接口比较
  • 相同点:
    • 两者都是接口,都可创建线程,都需要调用Thread.start()启动线程。
  • 不同点:
    • 实现Callable接口的线程能返回执行结果;而实现Runnable接口的线程不能返回结果;
    • Callable接口的call()方法允许抛出异常;而Runnable接口的run()方法的不允许抛异常;
    • 实现Callable接口的线程可以调用Future.cancel取消执行,而实现Runnable接口的线程不能;

你可能感兴趣的:(并发编程,java,学习,开发语言)