从设计的角度讨论Java中线程的两种创建方式

简介: Java中的多线程让我们的程序可以同时运行多个任务,即使我们的CPU是单核的。当然我们都明白这种情况下的同时运行,并不是真正的同时运行,而是JVM中的线程调度器根据时间片轮转的方式快速的在不同线程间的切换。

Java中的多线程让我们的程序可以同时运行多个任务,即使我们的CPU是单核的。当然我们都明白这种情况下的同时运行,并不是真正的同时运行,而是JVM中的线程调度器根据时间片轮转的方式快速的在不同线程间的切换。线程调度器让JVM一会运行这个线程,一会运行那个线程,切换的速度很快便我们产生了这些线程好像同时运行的假象。

线程的创建方式有两种,这两种方式究竟有什么不同?性能有什么差异?为什么要设计两种方式呢?带着这些疑问,我们首先回顾一下线程的两种创建方式。

第一种方式,通过实现Runnable接口来创建一个线程:

public class StudyThread{
	public static void main(String[] args){
		MyRunnable myRunnable = new MyRunnable();
		Thread thread = new Thread(myRunnable);
		thread.start();
		
	}
}

class MyRunnable implements Runnable{
	public void run(){
		System.out.println("第一种创建方式");
	}
}


第二种方式,通过继承Thread类来创建一个线程:

public class StudyThreadTwo{
	public static void main(String[] args){
		MyThread myThread = new MyThread();
		myThread.start();
	}
}

class MyThread extends Thread{
	public void run(){
		System.out.println("第二种启动方式");	
	}	
}


通过这两种方式,都能正常创建一个新的线程,性能方面我觉得应该没有多大差异,我们来从设计的角度看一下它们究竟有什么不同。

第一种创建方式是最经常被使用的创建方式。它创建一个线程,需要两个相关类,一个Java提供的Thread类,另一个是要我们自己实现的:实现Runnable接口,并实现run方法。我们要做的就是创建一个Thread对象,在创建时需要一个Runnable类型的参数是需要我们自己实现的。在实际中我们通常使用匿名内部类来实现这种方式,具体如下:

public class StudyThread{
	public static void main(String[] args){
		Thread thread = new Thread(new Runnable(){
			public void run(){
				System.out.println("使用匿名内部类创建线程");
			}
		});
		thread.start();
	}
}

第二种创建方式只需要一个类,就是我们继承自 Thread 类,然后重写 run 方法。这种创建方式相比第一种仿佛更简单。实际上这种简单是有代价的,如果我们从设计的角度(设计原则、设计模式)来考虑的话,就会明白我们为什么在实际中很少用这种方式。

根据设计原则中的单一职责原则,我们的类应该仅实现一类功能,应该有且仅有一个原因引起类的变更,应该让界面显示的类只负责界面显示,数据处理只负责数据处理,控制的只负责控制,最合适的例子就是MVC这种复合设计模式。我们再回到上面的分析,为了便于理解,我们可以将Thread类看做一个工人(a worker),Runnable(中的run方法)就是这个工人的工作(work),在第一种创建方式中,工人是工人,工作是工作(松耦合),工作(Runnable)的变更不会影响到工人(Thread);而在第二种创建方式中,工人与工作绑定死了(紧耦合),工作一变,工人也跟着变,这种牵一发而动全身的编程方式,在简单的工程中还好说,如果在一个及其庞大的项目中的话,如果你的一个地方的修改要引起整个系统的更改的话,那你与你的项目会一起崩溃的。

既然第二种相比第一种有如此弊端,为什么还要设计第一种呢?我觉得我们可以从继承的角度来理解。继承可以让子类自动共享父类的数据和方法,并允许子类添加新的数据或者方法。我们有时就是需要一个工作与工人绑定的,并如果被大量使用的,第二种方式就有优点了。不知道这种理解方式合适与否,欢迎大家积极参与讨论。

相关文章
|
6天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)
|
17天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
79 6
【Java学习】多线程&JUC万字超详解
|
2天前
|
Java
深入理解Java中的多线程编程
本文将探讨Java多线程编程的核心概念和技术,包括线程的创建与管理、同步机制以及并发工具类的应用。我们将通过实例分析,帮助读者更好地理解和应用Java多线程编程,提高程序的性能和响应能力。
15 4
|
10天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
2天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
Java 调度 开发者
Java中的多线程基础及其应用
【9月更文挑战第13天】本文将深入探讨Java中的多线程概念,从基本理论到实际应用,带你一步步了解如何有效使用多线程来提升程序的性能。我们将通过实际代码示例,展示如何在Java中创建和管理线程,以及如何利用线程池优化资源管理。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的见解和技巧,帮助你更好地理解和应用多线程编程。
|
12天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用
|
11天前
|
算法 Java 数据处理
Java并发编程:解锁多线程的力量
在Java的世界里,掌握并发编程是提升应用性能和响应能力的关键。本文将深入浅出地探讨如何利用Java的多线程特性来优化程序执行效率,从基础的线程创建到高级的并发工具类使用,带领读者一步步解锁Java并发编程的奥秘。你将学习到如何避免常见的并发陷阱,并实际应用这些知识来解决现实世界的问题。让我们一起开启高效编码的旅程吧!
|
16天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。