对于通过继承Thread类和实现Runnable接口这两种方式来创建线程的理解(含少量源码分析)_MrFengZH的博客-程序员秘密

技术标签: runnable  java  线程  thread  源码  

举例说明如何通过这两种方式创建线程

继承Thread类创建线程类

public class FirstThread extends Thread {
    
	int i = 0;	//不同线程对象不能共享该成员变量
	
	/**
	 * 重写run方法,该方法为线程的执行体
	 */
	@Override
	public void run() {
    
		for (; i < 10; i++) {
    
			System.out.println(getName() + ": i = " + i);
		}
	}
}
public class TestFirstThread {
    

	public static void main(String[] args) {
    
		new FirstThread().start();
		new FirstThread().start();
	}

}

在main方法中创建了两次该线程类,并调用start方法开启线程,然后执行线程中的run方法。

注意:使用继承Thread类创建线程类时,不同线程对象不能共享该线程类的成员变量

上述代码输出结果

Thread-0: i = 0
Thread-1: i = 0
Thread-1: i = 1
Thread-0: i = 1
Thread-1: i = 2
Thread-1: i = 3
Thread-1: i = 4
Thread-1: i = 5
Thread-1: i = 6
Thread-1: i = 7
Thread-1: i = 8
Thread-1: i = 9
Thread-0: i = 2
Thread-0: i = 3
Thread-0: i = 4
Thread-0: i = 5
Thread-0: i = 6
Thread-0: i = 7
Thread-0: i = 8
Thread-0: i = 9

实现Runnable接口创建线程

public class SecondThread implements Runnable {
    
	
	int i = 0;

	@Override
	public void run() {
    
		for (; i < 10; i++) {
    
			System.out.println(Thread.currentThread().getName() + ": i = " + i);
		}
	}

}
public class TestSecondThread {
    

	public static void main(String[] args) {
    
		SecondThread st = new SecondThread();	//作为target传入Thread的构造方法
		new Thread(st).start();
		new Thread(st).start();
	}

}

运行结果

Thread-0: i = 0
Thread-0: i = 1
Thread-1: i = 0
Thread-0: i = 2
Thread-0: i = 4
Thread-0: i = 5
Thread-0: i = 6
Thread-1: i = 3
Thread-1: i = 8
Thread-1: i = 9
Thread-0: i = 7

从运行结果可以发现用这种方法创建的多个线程可以共享线程类(严格来说不是线程类,只是和线程类一样实现了Runnable接口,说它是线程类的target更合适)的成员变量。下面会分析为什么会这样。

从源码角度分析以上两种方法

其实查看源码可以发现,以上这两种方法虽然实现不同,但内在的原理是一样的。

继承Thread

class Thread implements Runnable {
    
    //........
    
    @Override
    public void run() {
    
        if (target != null) {
    
            target.run();
        }
    }
}

可以看到Thread实现了Runnable接口并重写了run方法。FirstThread方法继承了Thread,其实也就是实现了Runnable接口,然后重写了run方法。
第一种方法开启线程是这样的:new FirstThread().start(); 它执行了Thread的无参构造方法

    public Thread() {
    
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

我们重点看第二个参数Runnable target,这里的target为null,也就是说第一种方式实现不需要target,但第二种方式会不一样。

实现Runnable

@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

可以看到Runnable接口只有一个方法,而SecondThread实现了该接口并重写了这个方法。所以实际上SecondThread也是一个Runnable,也就是说SecondThread可以作为Thread类的一个target。

第二种方法开启线程是这样的:new Thread(st).start(); 它执行了Thread的含有一个Runnable参数的构造方法

    public Thread(Runnable target) {
    
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

这里的第二个参数不在是null,而是传来的参数target,然后在init方法执行过后,SecondThread对象被赋给了Thread的成员变量

private Runnable target;

所以现在虽然Thread的run方法没有被重写,但它实际上是调用了target的run方法,也就是SecondThread中重写的run方法。不妨再看一次Thread重写的run方法:

    @Override
    public void run() {
    
        if (target != null) {
    
            target.run();
        }
    }

所以说,这两种方法的实质都是重写了Thread中的run方法,只不过是直接重写该方法和调用target所重写的该方法的区别。

两种方式所带来的不同

分析过源码后,现在就更加清楚为什么不同的实现,会产生成员变量是否可以共享的问题。

  • 首先是第一种方式,这里new了两个Thread的子类,也就是说有两个对象,等于实现了两个接口对象,那么接口中的run方法的变量自然不能共享了。
		new FirstThread().start();
		new FirstThread().start();
  • 然后是第二种,这里虽然也是new了两个Thread,但是只有一个接口对象,就是st,那么接口中的run方法的变量自然就可以共享了(因为就只有这一个接口对象,也就是说只有这个变量,当然只能共享了)。
		SecondThread st = new SecondThread();	//作为target传入Thread的构造方法
		new Thread(st).start();
		new Thread(st).start();

其实弄懂了这些后,我们也可以用第二种方式实现第一种的效果,只要多创建一个SecondThread(Runnable)对象就行了。

		SecondThread st = new SecondThread();
		SecondThread st1 = new SecondThread();
		new Thread(st).start();
		new Thread(st1).start();

改为上述代码之后,再运行就会和第一种方式的效果一样。原因很简单,这里多了一个SecondThread对象,两者的变量当然是不能共享的,因为根本就不在同一块存储空间。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fzhhsa/article/details/83213919

智能推荐

CMAKE错误:No CMAKE_CUDA_COMPILER could be found._luckwsm的博客-程序员资料_cmake_cuda_compiler

在使用cmake编译VTK程序过程中,执行cmake命令时,出现如下错误:-- The CUDA compiler identification is unknownCMake Error at CMakeLists.txt:4 (enable_language): No CMAKE_CUDA_COMPILER could be found. Tell CMake where to find the compiler by setting either the environment v...

Webrtc 源码分析起步_大飞飞鱼的博客-程序员资料_webrtc源码

本文是从Webrtc实际应用的方面介绍如何快速理解Webrtc的源码。Webrtc是用于web的实时通信框架,也可以直接使用Google开源的Webrtc实现来开发非Web的APP。要理解如何使用webrtc的源码,最好是先懂得Webrtc通信的流程,请查看用Webrtc实现免费的1对1高清实时视音频会议系统就可以了解Webrtc的接口使用流程,了解这个流程后就可以快速理解Google开源的那部分Webrtc源码了,并为自己使用。需要掌握Webrtc的js部分的PeerConnection,js部...

windows下qt程序意外崩溃,生成dump文件的方法_修行进行时的博客-程序员资料

程序运行一段时间之后,意外退出,没有提示任何出错信息,这种情况可能是内存泄漏,但是没有提示错误信息,很难定位到具体代码。我们可以生成dump文件,定位出错位置。在工程中添加代码:mdump.h#ifndef MDUMP1_H#define MDUMP1_H#include &lt;Windows.h&gt;#include &lt;DbgHelp.h&gt;// base...

Java开源模板引擎_wx5701619的博客-程序员资料_java 模板引擎原理

Velocity Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。 当Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由java程序

Unix/Linux下几个最致命的命令_虎皮猫大人王的博客-程序员资料

本文主要参考了以下原文:http://www.linuxpromagazine.com/Online/News/Seven-Deadliest-Linux-Commandshttp://ubuntuforums.org/announcement.php?f=326大多数时候程序员性情温和,神情淡定。但是会有例外,当他们因为加班加点过度劳累,在意识脆弱、头脑短路、麻痹大意时执行了

R语言应用实战-聚类分析以及k-means的优缺点_普通网友的博客-程序员资料_r型聚类分析的优缺点

一.聚类概念聚类分析cluster Analysis)是研究物以类聚的现代统计方法。在过去是依靠经验和专业知识做定性分析处理,很少利用数学方法多元统计分析逐渐被引进数值分类,形成聚类分析的分支。以下是我为大家准备的几个精品专栏,喜欢的小伙伴可自行订阅,你的支持就是我不断更新的动力哟!MATLAB-30天带你从入门到精通MATLAB深入理解高级教程(附源码)tableau可视化数据分析高级教程思想:认为所研究的样本或指标(变量)之间存在着不同程度的相似性(亲疏关系)。于是根据一批样本的

随便推点

CentOS7 安装 MYSQL5.6_LiexingVerne的博客-程序员资料

最近在学习虚拟机的下安装mysql,弄了很久最后终于搞好了(感觉自己太笨),记录下自己遇到的问题。环境:1.CentOS 7X642.mysql-5.6.45-linux-glibc2.12-x86_64.tar.gz安装步骤1.检查是否安装mariadbrpm -qa | grep mariadb如果存在就卸载rpm -e--nodeps mariadb...

@PostConstruct 与 ApplicationContextAware实现类冲突问题解决_Java充电桩的博客-程序员资料[email protected] applicationcontext

一般@PostConstruct 是可以在bean注册时执行的方法.而我们经常会需要用到spring上下文的一些参数,就可以用一个工具类如SpringContextUtilpublic class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplica

python爬取论坛付费内容_Python爬虫抓取论坛关键字过程解析_weixin_39914049的博客-程序员资料

前言:之前学习了用python爬虫的基本知识,现在计划用爬虫去做一些实际的数据统计功能。由于前段时间演员的诞生带火了几个年轻的实力派演员,想用爬虫程序搜索某论坛中对于某些演员的讨论热度,并按照日期统计每天的讨论量。这个项目总共分为两步:1.获取所有帖子的链接:将最近一个月内的帖子链接保存到数组中2.从回帖中搜索演员名字:从数组中打开链接,翻出该链接的所有回帖,在回帖中查找演员的名字获取所有帖子的链...

flutter 插件已经安装但是还报错_前端技术:一文带你掌握Flutter插件开发新姿势..._黄哒哒的博客-程序员资料

导语 |随着Flutter生态的快速发展,越来越多的Flutter插件涌现出来,那么如何开发一个Flutter插件呢?本文以具体的native_image_view插件为例,带你快速掌握Flutter插件的开发。文章作者:赵哲,腾讯游戏运营研发工程师一、概述随着开发技术的发展,几乎所有主流的开发语言都有自己的包管理工具。Node开发有npm、Android开发有Gradle,Flutter也有自己...

无法连接远程redis问题_LeBron永鑫的博客-程序员资料_redis最新版本无法远程连接

总结:1.允许redis外网访问开启这个配置时:redis 只接收来自于该 IP 地址列表的请求,如果不进行设置,那么将处理所有请求注释掉bind 127.0.0.1,允许redis外网访问2.关闭保护模式protected-mode改为no,默认为yes3.开放云服务器端口6379遇到的坑:千万不要使用宝塔面板开放端口!!!我使用宝塔面板开放了端口6379不起作用,打开阿里云控制台后才发现根本没有放行6379端口以后再开放端口一定要使用阿里云控制台!!!...

axios 重定向问题解决_Axios 源码解析(我总觉得我解析得最全了)_weixin_39731845的博客-程序员资料

准备工作主要根据他在 npm 上的核心功能点,进行提问吧。可以先抛出几个问题1、axios 的大致原理 or axios 的本质是什么2、axios 是怎么实现请求拦截的3、axios 是如何防止 xsrf 的4、实际使用中遇到过的点好,接下来围绕这些点对这个库简单分析一下,首先我们可以先写一个服务器,可以接收并打印前端请求的就可以const http = require('http');cons...

推荐文章

热门文章

相关标签