创建线程的四种方式(包括Executors创建线程池)

创建线程的四种方式

1、通过继承Thread类

2、通过实现Runnable接口

3、通过实现Callable接口

4、通过使用Executors工具类创建线程池

Thread

要继承Thread类实现创建线程,主要有以下步骤:

1、定义一个Thread子类MyThread,重写run()方法,实现相关逻辑,run()方法就是线程要执行的业务逻辑方法

public class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run()方法正在执
行...");
    }
}

2、创建自定义的线程子对象,比如MyThread的对象

3、调用子类MyThread实例的start()方法来启动线程

public class ThreadTest{
    public static void main(String[] args){
        MyThread myThread = new MyThread();
        myThread.start();
        System.out.println(Thread.currentThread().getName() + "main()方法执行结束");
    }
}

 运行结果

main main()方法执行结束

Thread-0 run()方法正在执行...

 Runnable

实现Runnable接口创建线程的步骤如下:

1、定义Runnable接口实现类MyRunnable,重写run()方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
    }
}

2、创建MyRunnable实例,以此实例作为target创建Thread对象,该Thread对象才是真正的线程对象

3、调用线程对象的start()方法

public class RunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
    }
}

执行结果

main main()方法执行完成

Thread-0 run()方法执行中... 

Callable

什么是 Callable 和 Future?

        Callable 接口类似于 Runnable,从名字就可以看出来了,但是 Runnable 不会 返回结果,并且无法抛出返回结果的异常,而 Callable 功能更强大一些,被线 程执行后,可以返回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。 Future 接口表示异步任务,是一个可能还没有完成的异步任务的结果。所以说 Callable用于产生结果,Future 用于获取结果。

什么是 FutureTask

        FutureTask 表示一个异步运算的任务。FutureTask 里面可以传入一个 Callable 的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判 断是否已经完成、取消任务等操作。只有当运算完成的时候结果才能取回,如果 运算尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了 Callable 和 Runnable 的对象进行包装,由于 FutureTask 也是Runnable 接口 的实现类,所以 FutureTask 也可以放入线程池中。

实现Callable接口创建线程步骤如下:

1、创建实现Callable接口的类myCallable

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
        return 1;
    }
}

2、以myCallable为参数创建FutureTask对象

3、将FutureTask作为参数创建Thread对象

4、调用线程对象的start()方法

public class CallableTest {
    public static void main(String[] args) {
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
        Thread thread = new Thread(futureTask);
        thread.start();
        try {
            Thread.sleep(1000);
            System.out.println("返回结果 " + futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
            System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
    }
}

Executors

        Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了 ExecutorService接口。 主要有newFixedThreadPool,newCachedThreadPool, newSingleThreadExecutor,newScheduledThreadPool,后续详细介绍这四 种线程池

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
    }
}
public class SingleThreadExecutorTest {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        MyRunnable runnableTest = new MyRunnable();
        for (int i = 0; i < 5; i++) {
            executorService.execute(runnableTest);
        }
        System.out.println("线程任务开始执行");
        executorService.shutdown();
    }
}

 执行结果

线程任务开始执行

pool‐1‐thread‐1 run()方法执行中...

pool‐1‐thread‐1 run()方法执行中...

pool‐1‐thread‐1 run()方法执行中...

pool‐1‐thread‐1 run()方法执行中...

pool‐1‐thread‐1 run()方法执行中...

 Runnable和Callable的区别

相同点:

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

主要区别 :

  • Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,是个泛 型,和Future、FutureTask配合可以用来获取异步执行的结果
  • Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息

注:Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法 会阻塞主进程的继续往下执行,如果不调用不会阻塞。

线程的run()和start()的区别

        每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的, run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。 start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以 重复调用,而 start() 只能调用一次。 start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待 run方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪 状态,并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, run()方法运行结束, 此线程终止。然后CPU再调度其它线程。 run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直 接调用run(),其实就相当于是调用了一个普通函数而已,直接调用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条, 根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方 法。 为什么我们调用 start() 方法时会执行 run() 方

那么,为什么我们调用start()方法后会执行run()方法呢?为什么不直接调用run呢?

        new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程 并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。 而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执 行,并不会在某个线程中执行它,所以这并不是多线程工作。 总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值