Java并发基础01. 传统线程技术中创建线程的两种方式
传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法;二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread。这两种方式大部分人可能都知道,但是为什么这样玩就可以呢?下面我们来详细分析一下这两种方法的来龙去脉。
1. 揭秘Thread中run() |
上面我们看到这两种方式都跟`run()`方法有关,所以我们来看一下`Thread`的源码中`run()`方法到底都干了什么:
```java
@Override
public void run() {
if (target != null) {
target.run();
}
}
```
我们可以看出,`run()`方法中很简单,只有一个`if`语句,如果target不为空就执行target的`run()`方法,否则什么也不干,那么这target到底是何方神圣呢?我们点击进去可以看到:
```java
private Runnable target;
```
原来target就是Runnable接口,我们再点进Runnable看看:
```java
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
```
Runnable中就一个方法,也是`run()`方法!好了,现在再回到Thread类的`run()`方法中,如果target不为空,即实现了Runnable接口,也即实现了Runnable中的`run()`方法,那么我们就使用该接口中的`run()`方法;如果target为空,即没有实现Runnable接口,那我们什么也不做,即线程创建后立马就消失了。
所以到这里,大家就明白了为什么创建线程有上面两种方式了。第一种:你不是要先进行`if`判断么?我现在不判断了,我把你的`if`干掉,我在`run()`方法中自己写代码,想干啥就干啥,即重写Thread中的`run()`方法,;第二种:你不是要先进行`if`判断么?行,给你一个Runnable接口让你判断,但你还是得调用我Runnable中的`run()`方法啊,那我重写我Runnable中的`run()`方法不就行了!
知道了来龙去脉后,下面就针对这两种传统的方式写个实例。
2. 创建方式1:继承Thread类 |
只要两步即可创建并开启一个线程:
- 继承
Thread类,并实现run()方法; - 调用
start()方法开启线程。
由于只要实现一个run()方法即可,所以我们可以使用java中的匿名内部类来实现,如下:
public class TraditionalThread {
public static void main(String[] args) {
/********** 第一种方法:继承Thread类,覆写run()方法 **************/
Thread thread1 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(500);//让线程休息500毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());//打印出当前线程名
}
};
thread1.start();//开启线程
}
}
3. 创建方式2:实现Runnable接口 |
只要两步即可创建并开启一个线程:
- 实现
Runnable接口,并实现run()方法; - 调用
start()方法开启线程。
由于只要实现一个run()方法即可,所以我们也可以使用java中的匿名内部类来实现,如下:
public class TraditionalThread {
public static void main(String[] args) {
/********** 第二种方法:实现Runnable接口,扔给Thread **************/
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
});
thread2.start();
}
}
4. 两种方式同时使用 |
如果有个哥们比较给力,他两种方式同时使用了,即:既实现了Thread类中的`run()`方法,又给Thread扔了一个实现了`run()`方法的Runnable。如下所示:
```java
public class TraditionalThread {
public static void main(String[] args) {
//这哥们的代码写的比较给力
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Runnable:" + Thread.currentThread().getName());
}
}){
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread:" + Thread.currentThread().getName());
}
}.start();
}
}
现在又会执行哪个呢?我们运行一下上面的程序就会发现,它会打印出Thread的信息,所以运行的是Thread的`run()`方法,知道结论了,但是为啥呢?
从面向对象的思想去考虑:上面一段代码其实是新new了一个对象(子对象)继承了Thread对象(父对象),在子对象里重写了父类的`run()`方法,父对象中扔了个Runnable进去,父对象中的`run()`方法就是最初的带有`if`判断的`run()`方法。
好了,现在执行`start()`后,肯定先在子类中找`run()`方法,找到了,父类的`run()`方法自然就被干掉了,所以会打印出Thread:,如果我们现在假设子类中没有重写`run()`方法,那么必然要去父类找`run()`方法,父类的`run()`方法中就得判断是否有Runnable传进来,现在有一个,所以执行Runnable中的`run()`方法,那么就会打印Runnable:出来。
OK,传统的创建线程的两种方式就总结这么多~如有错误之处,欢迎留言指正~
Java并发基础01. 传统线程技术中创建线程的两种方式的更多相关文章
- 【java并发】传统线程技术中创建线程的两种方式
传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...
- Java中创建String的两种方式
1.在Java中,创建一个字符串有两种方式 String x = "abc";String y = new String("abc"); 这两种方式有什么区别呢 ...
- java中创建字符串的两种方式(“”与new String())及区别
结论:通过""创建的字符串实际上在java堆中只有一个,而通过new string创建出来的字符串在java堆中占有不同的内存. 第一个True表明这两个在内存中拥有相同的地址,那 ...
- Java中创建String的两种方式差异
我们知道创建一个String类型的变量一般有以下两种方法: String str1 = "abcd"; String str2 = new String("abcd&qu ...
- Java中HashMap遍历的两种方式
Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...
- java中数组复制的两种方式
在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...
- jQuery中开发插件的两种方式
jQuery中开发插件的两种方式(附Demo) 做web开发的基本上都会用到jQuery,jQuery插件开发两种方式:一种是类扩展的方式开发插件,jQuery添加新的全局函数(jQuery的全局函数 ...
- mybatis中批量插入的两种方式(高效插入)
MyBatis简介 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...
- linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...
随机推荐
- 移动webApp必备技能一、WebApp 里Meta标签大全,webappmeta标签大全
1.先说说mate标签里的viewport: viewport即可视区域,对于桌面浏览器而言,viewport指的就是除去所有工具栏.状态栏.滚动条等等之后用于看网页的区域.对于传统WEB页面来说,9 ...
- vue+webpack工程环境搭建
使用Vue-cli脚手架(属于vue全家桶)快速构建一个项目: [1]首先需要安装好node.js; [2]安装webpack,指令$npm install -g webpack; //如果之前有安装 ...
- notepad++ 快捷键运行python程序目录存在空格的问题
通常情况下 cmd /k (python.exe文件所在路径) "$(FULL_CURRENT_PATH)"&PAUSE&EXIT 就ok了,路径里有空格就不一样了 ...
- 「从零单排HBase 06」你必须知道的HBase最佳实践
前面,我们已经打下了很多关于HBase的理论基础,今天,我们主要聊聊在实际开发使用HBase中,需要关注的一些最佳实践经验. 1.Schema设计七大原则 1)每个region的大小应该控制在10G到 ...
- 浅谈Java中静态代码块和非静态代码块
静态代码块: static{} 执行优先级高于非静态的初始化块,它会在类初始化(类初始化这个问题改天再详细讨论)的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员. 非静 ...
- centos-Linux静态IP地址配置
首先在VMware菜单中点击编辑-->虚拟网卡编辑器,查看NAT网段(子网掩码.网关.起止IP地址) 1.用nmcli命令配置IP地址 [root@Core ~]# nmcli connecti ...
- 我用STM32MP1做了个疫情监控平台2—Qt环境搭建
目录 1.嵌入式Qt简介 2.查看开发板Qt库的版本 3.主机搭建Qt环境 4.第一个Qt程序--Hello World 5.一些问题 @ 1.嵌入式Qt简介 Qt 是一个跨平台的应用程序开发框架.使 ...
- mysql8 修改root密码
Navicat工具里选中mysql数据库 执行: ALTER user 'root'@'localhost' IDENTIFIED BY 'newpassward'; //newpassward 新密 ...
- [Bugku]Web题解
bugku地址链接:https://ctf.bugku.com 1.web2 浏览器就显示一堆动态笑脸,时间长了密集恐惧症了. 解法1: F12查看源码 解法2: 地址栏输入: view-source ...
- 前端html学习笔记
一 HTML介绍 1. Web服务的本质 import socket sk = socket.socket() sk.bind(("127.0.0.1", 8080)) sk.li ...