Java创建线程的三种方式

本文详细介绍了在Java中创建线程的三种常见方法:继承Thread类、实现Runnable接口及实现Callable接口。通过示例代码展示了每种方法的具体实现过程,并对比了它们之间的优劣。

先给出图片版(标出关键代码),再给出文字版!

话不多说,直接上示例代码吧!

创建线程之继承Thread类

给出文字版:

/**
 * 创建线程 --- 测试
 *
 * @author JustryDeng
 * @date 2018/10/11 14:52
 */
public class CreatThreadByExtendsThread {
    public static void main(String[] args) {
        // 获取该线程实例
        MyThread myThread = new MyThread();
        // 运行该线程
        myThread.start();

        //主线程同时输出,以作对比
        for (int i = 1; i <= 100; i++) {
            Thread.currentThread().setName("main线程");
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }

    }


}

/**
 * 创建线程 -> 继承Thread类,重写run方法
 *
 * @date 2018/10/11 14:54
 */
class MyThread extends Thread{

    @Override
    public void run() {
        this.setName("JustryDeng线程");
        for (int i = 1; i <= 100; i++) {
            System.out.println(this.getName() + "->" + i);
        }
    }
}

注:这里只给出最基本的示例,灵活变形使用即可。

运行主函数,输出内容为:


创建线程之实现Runnable接口

给出文字版:

/**
 * 创建线程 ---- 测试
 *
 * @date 2018/10/11 14:54
 */
class Rtest {
    public static void main(String[] args) {
        // 获取该线程实例
        CreatThreadByImplementsRunnable ctb = new CreatThreadByImplementsRunnable();
        Thread thread = new Thread(ctb, "R线程");
        // 运行该线程
        thread.start();

        //主线程同时输出,以作对比
        for (int i = 1; i <= 100; i++) {
            Thread.currentThread().setName("main线程");
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }
    }
}

/**
 * 创建线程 -> 实现Runnable接口,重写run方法;
 *
 * @author JustryDeng
 * @date 2018/10/11 14:52
 */
public class CreatThreadByImplementsRunnable implements Runnable{

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }
    }
}

注:这里只给出最基本的示例,灵活变形使用即可。

运行主函数,输出内容为:


创建线程之实现Callable<V>接口

给出文字版:

import java.util.Random;
import java.util.concurrent.*;

/**
 * 创建线程 ---- 测试
 *
 * @date 2018/10/11 14:54
 */
class Ctest {
    private static Integer count;
    public static void main(String[] args) {
        fa1();
        // fa2();
    }

    public static void fa1(){
        // 获取该线程实例
        CreatThreadByImplementsCallable ctbic = new CreatThreadByImplementsCallable();
        ctbic.setName("C线程!");
        // 使用Executors调度器,创建有3个空闲线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 将Callable<V>实现类的实例提交到线程池中,如果线程池中有空的线程,那么执行此实例的call()方法
        executorService.submit(ctbic);
        // 线程池中的所有线程都运行完毕后,关闭线程池
        executorService.shutdown();
        //主线程同时输出,以作对比
        for (int i = 1; i <= 100; i++) {
            Thread.currentThread().setName("main线程");
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }
    }

    public static void fa2(){
        // 获取该线程实例
        CreatThreadByImplementsCallable ctbic = new CreatThreadByImplementsCallable();
        ctbic.setName("C线程!");
        // 使用Executors调度器,创建有3个空闲线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 将Callable<V>实现类的实例提交到线程池中,如果线程池中有空的线程,那么执行此实例的call()方法
        // Future<V>接口与Callable<V>搭配使用,future.get()用于等待call()方法执行完毕并接受call()方法的返回值
        Future<Integer> future =  executorService.submit(ctbic);
        try {
            // 注意:future.get()会使当前线程阻塞,一致等到call()执行完毕返回返回值之后,当前线程再往下执行
            System.out.println("ctbic实例的call()方法的返回值为:" + future.get());
            // 线程池中的所有线程都运行完毕后,关闭线程池
            executorService.shutdown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //主线程同时输出,以作对比
        for (int i = 1; i <= 100; i++) {
            Thread.currentThread().setName("main线程");
            System.out.println(Thread.currentThread().getName() + "->" + i);
        }
    }
}

/**
 * 创建线程 -> 实现Callable<V>接口,call方法
 * 注:指定泛型,就是制定call()方法的返回值类型
 * 注:不指定泛型,那么默认泛型为Object
 *
 * @author JustryDeng
 * @date 2018/10/11 14:52
 */
public class CreatThreadByImplementsCallable implements Callable<Integer> {
    /* 线程名 */
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 相比起其他两种方式,此方式的优势在于:
     *    1. 有返回值
     *    2. 可以抛出异常
     */
    @Override
    public Integer call() throws Exception {
        // 随机获得[0,1000)中的整数
        Integer num = new Random().nextInt(1000);
        if(num <= 3){
            throw new Exception();
        }
        //主线程同时输出,以作对比
        for (int i = 1; i <= num; i++) {
            System.out.println(name + "->" + i);
        }
        return num;
    }
}

注:类似的还有FutureTask<V>,FutureTask<V>就像一个Runnable、Callable<V>、Future<V>的集合版,感兴趣的可
       自己去了解一下,这里就不再细说。

注:这里只给出最基本的示例,灵活变形使用即可。

注:ExecutorService的shutdown();方法是指:当线程池中的所有线程(包括排着队的)都执行完毕后,再关闭线程池。如果
       执行到shutdown()方法时,线程池中的所有线程正在执行任务,且还有100个任务排着队;那么只有当线程池中的线程
       把这100个排着队的线程执行完毕之后,才会关闭线程池。
P.S.此处不易解释,推荐直接查看ExecutorService源码(注释)。

注:ExecutorService的shutdownNow();方法是指:当线程池中的所有(正在执行着的)线程都执行完毕后,再关闭线程池。
       如果执行到shutdownNow()方法时,线程池中的所有线程正在执行任务,且还有100个任务排着队;那么当线程中的
       线程执行完正在执行的线程后,就不再“接新的线程任务”了,等到线程中的所有线程都空闲下来了,那么直接关闭线程池,
       并不理会那100个排着队的线程任务,让他们死去吧。
P.S.此处不易解释,推荐直接查看ExecutorService源码(注释)。

注:shutdown()、shutdownNow()方法都不会阻塞当前所在线程。

主函数中将fa2()注释掉,运行fa1(),控制台输出结果为:

主函数中将fa1()注释掉,运行fa2(),控制台输出结果为:

给出一个多线程使用Callable<V>、Future<V>的实例参考:


三种方式的比较

注:虽然实现Callable<V>接口开启线程的方式,相对执行效率低;但是对比起带来的效益,低那么点效率是完全值得的。

 

笔者将本人多线程一栏中博客涉及到的所有代码示例(Lock分开放在一个专门的项目、synchronized的代码附在该文章末尾),放在GIT上了(链接见本文末),这里先给出一个所涉及内容图:

 

声明:本文是学习笔记,主要学习自以下视频

^_^ 如有不当之处,欢迎指正

^_^ 学习视频
           《Java多线程与并发实战视频课程》,齐毅

^_^ 参考笔记
           《程序员成长笔记(一)》,笔者JustryDeng

^_^ 多线程一栏所有测试示例代码,托管链接
               https://github.com/JustryDeng/PublicRepository

^_^ 本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值