作者:唐彤
简介
创建线程,是多线程编程中最基本的操作,彤哥总结了一下,大概有8种创建线程的方式,你知道吗?
1.继承Thread类并重写run()方法
public class CreatingThread01 extends Thread { @Override public void run() { System.out.println(getName() + " is running"); } public static void main(String[] args) { new CreatingThread01().start(); new CreatingThread01().start(); new CreatingThread01().start(); new CreatingThread01().start(); } }
继承Thread类并重写run()方法,这种方式的弊端是一个类只能继承一个父类,如果这个类本身已经继承了其它类,就不能使用这种方式了。
2.实现Runnable接口
public class CreatingThread02 implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } public static void main(String[] args) { new Thread(new CreatingThread02()).start(); new Thread(new CreatingThread02()).start(); new Thread(new CreatingThread02()).start(); new Thread(new CreatingThread02()).start(); } }
实现Runnable接口,这种方式的好处是一个类可以实现多个接口,不影响其继承体系。
3.匿名内部类
public class CreatingThread03 { public static void main(String[] args) { // Thread匿名类,重写Thread的run()方法 new Thread() { @Override public void run() { System.out.println(getName() + " is running"); } }.start(); // Runnable匿名类,实现其run()方法 new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } }).start(); // 同上,使用lambda表达式函数式编程 new Thread(()->{ System.out.println(Thread.currentThread().getName() + " is running"); }).start(); } }
使用匿名类的方式,一是重写Thread的run()方法,二是传入Runnable的匿名类,三是使用lambda方式,现在一般使用第三种(java8+),简单快捷。
4.实现Callabe接口
public class CreatingThread04 implements Callable<Long> { @Override public Long call() throws Exception { Thread.sleep(2000); System.out.println(Thread.currentThread().getId() + " is running"); return Thread.currentThread().getId(); } public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<Long> task = new FutureTask<>(new CreatingThread04()); new Thread(task).start(); System.out.println("等待完成任务"); Long result = task.get(); System.out.println("任务结果:" + result); } }
实现Callabe接口,可以获取线程执行的结果,FutureTask实际上实现了Runnable接口。
5.定时器(java.util.Timer)
public class CreatingThread05 { public static void main(String[] args) { Timer timer = new Timer(); // 每隔1秒执行一次 timer.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } }, 0 , 1000); } }
使用定时器java.util.Timer可以快速地实现定时任务,TimerTask实际上实现了Runnable接口。
6.线程池
public class CreatingThread06 { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(5); for (int i = 0; i < 100; i++) { threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + " is running")); } } }
使用线程池的方式,可以复用线程,节约系统资源。
7.并行计算(Java8+)
public class CreatingThread07 { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 串行,打印结果为12345 list.stream().forEach(System.out::print); System.out.println(); // 并行,打印结果随机,比如35214 list.parallelStream().forEach(System.out::print); } }
使用并行计算的方式,可以提高程序运行的效率,多线程并行执行。
8.Spring异步方法
首先,springboot启动类加上 @EnableAsync注解(@EnableAsync是spring支持的,这里方便举例使用springboot)。
@SpringBootApplication @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
其次,方法加上 @Async注解。
@Service public class CreatingThread08Service { @Async public void call() { System.out.println(Thread.currentThread().getName() + " is running"); } }
然后,测试用例直接跟使用一般的Service方法一模一样。
@RunWith(SpringRunner.class) @SpringBootTest(classes = Application.class) public class CreatingThread08Test { @Autowired private CreatingThread08Service creatingThread08Service; @Test public void test() { creatingThread08Service.call(); creatingThread08Service.call(); creatingThread08Service.call(); creatingThread08Service.call(); } }
运行结果如下:
task-3 is running task-2 is running task-1 is running task-4 is running
可以看到每次执行方法时使用的线程都不一样。
使用Spring异步方法的方式,可以说是相当地方便,适用于前后逻辑不相关联的适合用异步调用的一些方法,比如发送短信的功能。
总结
(1)继承Thread类并重写run()方法;
(2)实现Runnable接口;
(3)匿名内部类;
(4)实现Callabe接口;
(5)定时器(java.util.Timer);
(6)线程池;
(7)并行计算(Java8+);
(8)Spring异步方法;
福利
上面介绍了那么多创建线程的方式,其实本质上就两种,一种是继承Thread类并重写其run()方法,一种是实现Runnable接口的run()方法,那么它们之间到底有什么联系呢?
请看下面的例子,同时继承Thread并实现Runnable接口,应该输出什么呢?
public class CreatingThread09 { public static void main(String[] args) { new Thread(()-> { System.out.println("Runnable: " + Thread.currentThread().getName()); }) { @Override public void run() { System.out.println("Thread: " + getName()); } }.start(); } }
说到这里,我们有必要看一下Thread类的源码:
public class Thread implements Runnable { // Thread维护了一个Runnable的实例 private Runnable target; public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { // ... // 构造方法传进来的Runnable会赋值给target this.target = target; // ... } @Override public void run() { // Thread默认的run()方法,如果target不为空,会执行target的run()方法 if (target != null) { target.run(); } } }
看到这里是不是豁然开朗呢?既然上面的例子同时继承Thread并实现了Runnable接口,根据源码,实际上相当于重写了Thread的run()方法,在Thread的run()方法时实际上跟target都没有关系了。
所以,上面的例子输出结果为 Thread:Thread-0,只输出重写Thread的run()方法中的内容。
最后
欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

8种创建Java线程的方式,你知道几个?的更多相关文章

  1. 5.创建执行线程的方式之三 :实现Callable 接口

    Callable 接口 一.Java 5.0 在 java.util.concurrent 提供了 一个新的创建执行线程的方式(之前有继承Thread 和 实现Runnable):Callable 接 ...

  2. 一步一步创建JAVA线程

    (一)创建线程 要想明白线程机制,我们先从一些基本内容的概念下手. 线程和进程是两个完全不同的概念,进程是运行在自己的地址空间内的自包容的程序,而线程是在进程中的一个单一的顺序控制流,因此,单个进程可 ...

  3. 按照阿里巴巴规范创建Java线程池

    前言 Executors Executors 是一个Java中的工具类.提供工厂方法来创建不同类型的线程池. 常用方法: 1.newSingleThreadExecutor   介绍:创建一个单线程的 ...

  4. Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信

    1.Java使用Thread类代表线程.     所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...

  5. 阶段3 2.Spring_03.Spring的 IOC 和 DI_6 spring中bean的细节之三种创建Bean对象的方式

    目前这里能调用是因为,在service的实现类里面,new了一个dao的对象 正常情况下 这里不应该是new一个对象,应该等于null或为空 设置为空侯再运行就会报错 出错的原因是这里为null 需要 ...

  6. 创建Java线程池

    线程池的作用: 线程池作用就是限制系统中执行线程的数量. 根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果:少了浪费了系统资源,多了造成系统拥挤效率不高.用线程池控制线程数量,其他线 ...

  7. spring:bean的细节之三种创建Bean对象的方式

    <!--创建Bean的三种方式--><!--第一种方式,使用默认构造函数创建 在spring的配置文件中使用bean标签,配以id和class属性之后,且没有属性和标签时. 采用的就 ...

  8. SPC2014 :“FOSL”不是替代InfoPath,只是另外一种创建表单的方式

    今天在SPC2014微软宣布他们技术路线图.其实,没有足够证据替代InfoPath,只是另外的一种尝试 - FOSL(对SharePoint列表表单). FOSL使用相同的引擎,用于创建表单的访问服务 ...

  9. Java线程之创建线程

    翻译自:https://www.journaldev.com/1016/java-thread-example 进程 进程是一个自包含的执行环境,它可以被看成一个程序或应用程序.然而一个应用程序本身包 ...

随机推荐

  1. Kotlin Coroutines不复杂, 我来帮你理一理

    Coroutines 协程 最近在总结Kotlin的一些东西, 发现协程这块确实不容易说清楚. 之前的那篇就写得不好, 所以决定重写. 反复研究了官网文档和各种教程博客, 本篇内容是最基础也最主要的内 ...

  2. 用户环境变量 shell变量 别名

    常见用户环境变量: 环境变量 说明 LANG   HOME   LOGNAME 用户名 PATH   SHELL   PWD   查看环境变量用:env或者echo $LANG 设置用户环境变量:ex ...

  3. react-router-dom路由

  4. 20191121-6 Scrum立会报告+燃尽图 02

    此作业的要求参见https://edu.cnblogs.com/campus/nenu/2019fall/homework/10066一.小组情况 队名:扛把子 组长:孙晓宇 组员:宋晓丽 梁梦瑶 韩 ...

  5. 程序员用于机器学习数据科学的3个顶级 Python 库

    NumPy NumPy(数值 Python 的简称)是其中一个顶级数据科学库,它拥有许多有用的资源,从而帮助数据科学家把 Python 变成一个强大的科学分析和建模工具.NumPy 是在 BSD 许可 ...

  6. mysql--时区表问题(Windows环境下)

    自己用Django开发个人博客是,数据库用的是mysql,期间遇到一个时间不一致的问题,具体解决过程: 1.问题原因:Windows没有时区表 2.去mysql官网下载相应版本的时区表:https:/ ...

  7. css+js相关笔记

    作者:故事我忘了c个人微信公众号:程序猿的月光宝盒 css部分: 1.内联元素垂直居中的设置: (1) 设置父级元素的行高 line-height,和高度 height ​ 原则:line-heigh ...

  8. supervisor 安装配置详解

    一.安装 源码安装 先下载最新的supervisor安装包:https://pypi.python.org/pypi/supervisor , 如: (python3命令为 pip install g ...

  9. firefox常用插件总结

    当初因为ctf比赛而接触啦firefox,慢慢的在firfox上安装满啦各种插件,今天就想着总结一下,给那些刚玩firefox的朋友一些小小的帮助也是好的. 1:Firebug    Firefox强 ...

  10. ExtentTestNGIReporterListener

    package com.testng.config; import com.aventstack.extentreports.ExtentReports; import com.aventstack. ...