传统的线程技术中有两种创建线程的方式:一是继承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. 传统线程技术中创建线程的两种方式的更多相关文章

  1. 【java并发】传统线程技术中创建线程的两种方式

    传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...

  2. Java中创建String的两种方式

    1.在Java中,创建一个字符串有两种方式 String x = "abc";String y = new String("abc"); 这两种方式有什么区别呢 ...

  3. java中创建字符串的两种方式(“”与new String())及区别

    结论:通过""创建的字符串实际上在java堆中只有一个,而通过new string创建出来的字符串在java堆中占有不同的内存. 第一个True表明这两个在内存中拥有相同的地址,那 ...

  4. Java中创建String的两种方式差异

    我们知道创建一个String类型的变量一般有以下两种方法: String str1 = "abcd"; String str2 = new String("abcd&qu ...

  5. Java中HashMap遍历的两种方式

    Java中HashMap遍历的两种方式 转]Java中HashMap遍历的两种方式原文地址: http://www.javaweb.cc/language/java/032291.shtml 第一种: ...

  6. java中数组复制的两种方式

    在java中数组复制有两种方式: 一:System.arraycopy(原数组,开始copy的下标,存放copy内容的数组,开始存放的下标,需要copy的长度); 这个方法需要先创建一个空的存放cop ...

  7. jQuery中开发插件的两种方式

    jQuery中开发插件的两种方式(附Demo) 做web开发的基本上都会用到jQuery,jQuery插件开发两种方式:一种是类扩展的方式开发插件,jQuery添加新的全局函数(jQuery的全局函数 ...

  8. mybatis中批量插入的两种方式(高效插入)

    MyBatis简介 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...

  9. linux内核分析作业4:使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    系统调用:库函数封装了系统调用,通过库函数和系统调用打交道 用户态:低级别执行状态,代码的掌控范围会受到限制. 内核态:高执行级别,代码可移植性特权指令,访问任意物理地址 为什么划分级别:如果全部特权 ...

随机推荐

  1. apache搭建Tomcat集群(Cluster)

    搭建集群: apache:特点处理静态资源(html  图片  js等) apache的请求操作,Cluster工具 tomcat:特点处理动态资源 apache+tomcat(apache是web服 ...

  2. 304 Not Modified

    304 Not Modified,不是服务器发出的错误,是服务器所承载的业务系统在开发时为了节省带宽和提升浏览器的体验,对GET/js,css,image等执行了缓存机制.客户端第一次对服务器发出GE ...

  3. jwt的token如何使用

    JWT简介: JWT(JSON WEB TOKEN):JSON网络令牌,JWT是一个轻便的安全跨平台传输格式,定义了一个紧凑的自包含的方式在不同实体之间安全传输信息(JSON格式).它是在Web环境下 ...

  4. python.五角星

    import turtle turtle.pensize(4)turtle.pencolor("black") turtle.fillcolor("red")t ...

  5. go学习第五天、运算符

    算术运算符 下表列出了所有Go语言的算术运算符.假定 A 值为 10,B 值为 20 运算符 描述 实例 + 相加 A + B 输出结果 30 - 相减 A - B 输出结果 -10 * 相乘 A * ...

  6. Simulink仿真入门到精通(十三) Simulink创建自定义库

    当用户自定义了一系列模块之后,可以自定义模块库将同类自定义模块显示到Simulink Browser中,作为库模块方便地拖曳到新建模型中. 建立这样的自定义库需要3个条件: 建立library的mdl ...

  7. python 异步请求

    这是循环请求10次页面.总时间大概是10秒左右,如果是普通的循环请求10次页面而不添加异步的话,时间大概在30秒以上,当然这个数据可能有误,因为有网速的问题存在,但大体的效果应该是不变的. impor ...

  8. 5W2H方法:七问分析法

    5W2H分析方法也叫七问分析法,是二战中美国陆军兵器修理部首创.简单.方便.易于理解.使用,富有启发意义,被广泛应用于企业管理和技术活动,对于决策和执行性的措施也非常有帮助,有助于弥补考虑问题的疏漏. ...

  9. 01.UNIX基础知识

    1.UNIX体系结构 什么叫做内核? 内核是一种软件,它控制计算机硬件资源,并提供程序的运行环境. 什么叫操作系统? 在广义上,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,如可以 ...

  10. win10环境下如何修改Python pip的更新源?

    1.在window的文件夹窗口输入 : %APPDATA%2.在弹出的路径中新建pip文件夹,然后到pip文件夹里面去新建个pip.ini文件,然后再里面输入内容 [global] timeout = ...