【Java】线程、线程安全、线程状态(一)

简介: 本期主要介绍线程、线程安全、线程状态

第一章 线程


1.1 多线程原理


昨天的时候我们已经写过一版多线程的代码,很多同学对原理不是很清楚,那么我们今天先画个多线程执行时序来体现一下多线程程序的执行流程。

代码如下:

自定义线程类:

image.png

image.png

流程图:

image.png

程序启动运行 main 时候, java 虚拟机启动一个进程,主线程 main 在 main() 调用时候被创建。随着调用 mt 的对象的start 方法,另外一个新的线程也启动了,这样,整个应用就在多线程下运行。

通过这张图我们可以很清晰的看到多线程的执行流程,那么为什么可以完成并发执行呢?我们再来讲一讲原理。

多线程执行时,到底在内存中是如何运行的呢?以上个程序为例,进行图解说明:

多线程执行时,在栈内存中,其实 每一个执行线程都有一片自己所属的栈内存空间 。进行方法的压栈和弹栈。

image.png

当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进

程就结束了。

1.2 Thread


在上一天内容中我们已经可以完成最基本的线程开启,那么在我们完成操作过程中用到了

java.lang.Thread 类,

API 中该类中定义了有关线程的一些方法,具体如下:

构造方法:

public Thread() : 分配一个新的线程对象。

public Thread(String name) : 分配一个指定名字的新的线程对象。

public Thread(Runnable target) : 分配一个带有指定目标新的线程对象。

public Thread(Runnable target,String name) : 分配一个带有指定目标新的线程对象并指定名字。

常用方法:

public String getName() : 获取当前线程名称。

public void start() : 导致此线程开始执行 ; Java 虚拟机调用此线程的 run 方法。

public void run() : 此线程要执行的任务在此处定义代码。

public static void sleep(long millis) : 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。

public static Thread currentThread() : 返回对当前正在执行的线程对象的引用。

翻阅 API 后得知创建线程的方式总共有两种,一种是继承 Thread 类方式,一种是实现 Runnable 接口方式,方式一我们上一天已经完成,接下来讲解方式二实现的方式。

1.3 创建线程方式二


采用 java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

1. 定义 Runnable 接口的实现类,并重写该接口的 run() 方法,该 run() 方法的方法体同样是该线程的线程执行体。

2. 创建 Runnable 实现类的实例,并以此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。

3. 调用线程对象的 start() 方法来启动线程。

代码如下:

image.png

通过实现 Runnable 接口,使得该类有了多线程类的特征。 run() 方法是多线程程序的一个执行目标。所有的多线程代码都在 run 方法里面。 Thread 类实际上也是实现了 Runnable 接口的类。

在启动的多线程的时候,需要先通过 Thread 类的构造方法 Thread(Runnable target) 构造出对象,然后调用 Thread对象的 start() 方法来运行多线程代码。

实际上所有的多线程代码都是通过运行 Thread 的 start() 方法来运行的。因此,不管是继承 Thread 类还是实现Runnable 接口来实现多线程,最终还是通过 Thread 的对象的 API 来控制线程的,熟悉 Thread 类的API 是进行多线程编程的基础。

tips:Runnable 对象仅仅作为 Thread 对象的 target , Runnable 实现类里包含的 run() 方法仅作为线程执行体。

而实际的线程对象依然是 Thread 实例,只是该 Thread 线程负责执行其 target 的 run() 方法。

1.4 ThreadRunnable的区别


如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。

总结:

实现Runnable接口比继承Thread类所具有的优势:

1. 适合多个相同的程序代码的线程去共享同一个资源。

2. 可以避免 java 中的单继承的局限性。

3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。

4. 线程池只能放入实现 Runable 或 Callable 类线程,不能直接放入继承 Thread 的类。

扩充:在 java 中,每次程序运行至少启动 2 个线程。一个是 main 线程,一个是垃圾收集线程。因为每当使用java 命令执行一个类的时候,实际上都会启动一个 JVM ,每一个 JVM 其实在就是在操作系统中启动了一个进

1.5 匿名内部类方式实现线程的创建


使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。

使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:

image.png

第二章 线程安全


2.1 线程安全


如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

我们通过一个案例,演示线程的安全问题:

电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “ 葫芦娃大战奥特曼 ” ,本次电影的座位共 100 个

( 本场电影只能卖 100 张票 ) 。

我们来模拟电影院的售票窗口,实现多个窗口同时卖 “ 葫芦娃大战奥特曼 ” 这场电影票 ( 多个窗口一起卖这 100 张票 )

需要窗口,采用线程对象来模拟;需要票, Runnable 接口子类来模拟

模拟票:

image.png

image.png

image.png

发现程序出现了两个问题:

1. 相同的票数 , 比如 5 这张票被卖了两回。

2. 不存在的票,比如 0 票与 -1 票,是不存在的。

这种问题,几个窗口 ( 线程 ) 票数不同步了,这种问题称为线程不安全。

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。

相关文章
|
2天前
|
Java 机器人 程序员
Java中的线程通信:wait、notify与Condition详解
Java中的线程通信:wait、notify与Condition详解
|
2天前
|
存储 安全 Java
Java中的线程安全与同步技术
Java中的线程安全与同步技术
|
1天前
|
存储 设计模式 并行计算
CopyOnWriteArrayList:深入理解Java中的线程安全List原理和应用
CopyOnWriteArrayList:深入理解Java中的线程安全List原理和应用
7 0
|
1天前
|
Java 测试技术 开发者
Java并发编程:深入理解线程池
本文将带领读者深入了解Java中的线程池,探索其内部机制、使用场景以及如何有效地利用线程池来提高程序的性能和可维护性。我们将通过实例演示如何创建和配置线程池,并讨论常见的并发模式和最佳实践。文章旨在为开发者提供实用的线程池应用知识,帮助他们在面对多线程编程挑战时,能够设计出更加高效和稳定的系统。
|
2天前
|
安全 Java 开发者
如何在Java中实现线程安全的单例模式
如何在Java中实现线程安全的单例模式
|
2天前
|
缓存 监控 安全
深入理解Java中的线程池和并发编程
深入理解Java中的线程池和并发编程
|
2天前
|
设计模式 安全 Java
如何在Java中实现线程安全的单例模式
如何在Java中实现线程安全的单例模式
|
2天前
|
缓存 安全 Java
如何使用Java实现高效的多线程编程
如何使用Java实现高效的多线程编程
|
2天前
|
安全 Java 机器人
Java中的多线程编程实用指南
Java中的多线程编程实用指南
|
1月前
|
安全 Java
深入理解Java并发编程:线程安全与性能优化
【2月更文挑战第22天】在Java并发编程中,线程安全和性能优化是两个重要的主题。本文将深入探讨这两个主题,包括线程安全的基本概念,如何实现线程安全,以及如何在保证线程安全的同时进行性能优化。
26 0