从设计的角度讨论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);而在第二种创建方式中,工人与工作绑定死了(紧耦合),工作一变,工人也跟着变,这种牵一发而动全身的编程方式,在简单的工程中还好说,如果在一个及其庞大的项目中的话,如果你的一个地方的修改要引起整个系统的更改的话,那你与你的项目会一起崩溃的。

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

相关文章
|
5天前
|
Java 程序员 开发者
深入理解Java并发编程:线程同步与锁机制
【4月更文挑战第30天】 在多线程的世界中,确保数据的一致性和线程间的有效通信是至关重要的。本文将深入探讨Java并发编程中的核心概念——线程同步与锁机制。我们将从基本的synchronized关键字开始,逐步过渡到更复杂的ReentrantLock类,并探讨它们如何帮助我们在多线程环境中保持数据完整性和避免常见的并发问题。文章还将通过示例代码,展示这些同步工具在实际开发中的应用,帮助读者构建对Java并发编程深层次的理解。
|
5天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第30天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,了解其工作原理和优势,然后详细介绍如何使用Java的Executor框架创建和管理线程池。最后,我们将讨论一些高级主题,如自定义线程工厂和拒绝策略。通过本文的学习,你将能够更好地理解和使用Java的线程池,提高你的并发编程能力。
|
5天前
|
安全 Java 调度
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第30天】本文将深入探讨Java并发编程的核心概念,包括线程安全、同步机制、锁优化以及性能调优。我们将通过实例分析如何确保多线程环境下的数据一致性,同时介绍一些常见的并发模式和最佳实践,旨在帮助开发者在保证线程安全的同时,提升系统的性能和响应能力。
|
2天前
|
存储 缓存 前端开发
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
13 3
|
3天前
|
Java
JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识
JAVA难点包括异常处理、多线程、泛型和反射,以及复杂的分布式系统知识。入坑JAVA因它的面向对象特性、平台无关性、强大的标准库和活跃的社区支持。
14 2
|
3天前
|
Java 调度 开发者
Java中的多线程编程:基础与实践
【5月更文挑战第2天】本文将深入探讨Java中的多线程编程,从基础概念到实际应用,为读者提供全面的理解和实践指导。我们将首先介绍线程的基本概念和重要性,然后详细解析Java中实现多线程的两种主要方式:继承Thread类和实现Runnable接口。接着,我们将探讨线程同步的问题,包括synchronized关键字和Lock接口的使用。最后,我们将通过一个实际的生产者-消费者模型来演示多线程编程的实践应用。
|
3天前
|
安全 Java 程序员
Java中的多线程编程:从理论到实践
【5月更文挑战第2天】 在计算机科学中,多线程编程是一项重要的技术,它允许多个任务在同一时间段内并发执行。在Java中,多线程编程是通过创建并管理线程来实现的。本文将深入探讨Java中的多线程编程,包括线程的概念、如何创建和管理线程、以及多线程编程的一些常见问题和解决方案。
12 1
|
4天前
|
存储 安全 Java
深入理解Java并发编程:线程安全与性能优化
【5月更文挑战第1天】本文将深入探讨Java并发编程的核心概念,包括线程安全和性能优化。我们将详细分析线程安全问题的根源,以及如何通过合理的设计和编码实践来避免常见的并发问题。同时,我们还将探讨如何在保证线程安全的前提下,提高程序的并发性能,包括使用高效的同步机制、减少锁的竞争以及利用现代硬件的并行能力等技术手段。
|
4天前
|
并行计算 Java 数据处理
Java中的多线程编程:基础知识与实践
【5月更文挑战第1天】本文将深入探讨Java中的多线程编程,包括其基本概念、实现方式以及实际应用。我们将从理论和实践两个角度出发,详细解析线程的创建、启动、控制以及同步等关键问题,并通过实例代码演示如何在Java中有效地使用多线程。
|
4天前
|
Java 程序员
Java中的多线程编程:从理论到实践
【5月更文挑战第1天】 在现代计算机科学中,多线程编程是一个重要的概念,它允许程序员在同一程序中并行运行多个任务。Java作为一种广泛使用的编程语言,提供了一套丰富的多线程编程工具。本文将介绍Java中多线程编程的基本概念,包括线程的创建、启动、控制和同步,以及一些常见的多线程问题和解决方案。