首页 > 编程语言 >并发编程 - 第三章

并发编程 - 第三章

时间:2024-07-03 19:55:26浏览次数:21  
标签:调用 第三章 编程 并发 线程 notify 方法 守护 wait

线程基础机制

1.1 守护线程Daemon

守护线程可以简单地理解为后台运行线程。进程结束,守护线程自然而然地就会结束,不需要手动的去关心和通知其状态。例如:在应用程序运行时播放背景音乐,在文字编辑器里做自动语法检查、自动保存等功能。Java的垃圾回收也是一个守护线程。守护线程的好处就是不需要关心它的结束问题。例如应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,不仅要退出主线程,还要通知播放背景音乐的线程退出,如果设定为守护线程则不需要了。

守护线程与普通线程写法上基本没啥区别,调用线程对象的方法setDaemon(tnue),则可以将其设置为守护线程,该方法必须在启动线程前调用。守护线程程序主动使用的情况较少,但在JVM 的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等使用情况。

JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,当进程中所有非守护线程已结束或退出时,即使仍有守护线程在运行,进程仍将结束。也就是说当程序结束了,守护线程有可能依然还未退出。

1.2 线程组ThreadGroup

ThreadGroup 是为了方便线程管理出现的。我们可以统一设定线程组的一些属性,比如setDaemon,设置未处理异常的处理方法,设置统一的安全策略等等,也可以通过线程组方便地获得线程的一些信息。

每一个 ThreadGroup都可以包含一组的子线程和一组子线程组,在一个进程中线程组是以树形的方式存在,通常情况下根线程组是system 线程组。system 线程组下是 main 线程组,默认情况下第一级应用自己的线程组是通过 main 线程组创建出来的。也就是说system 线程组是所有线程最顶级的父线程组。

Java允许我们对一个线程组中的所有线程同时进行操作,比如我们可以通过调用线程组的相应方法来设置其中所有线程的优先级,也可以启动或阻塞其中的所有线程。Java的多线程组机制的另一个重要作用是线程安全。线程组机制允许我们通过分组来区分有不同安全特性的线程,对不同组的线程进行不同的处理,还可以通过线程组的分层结构来支持不对等安全措施的采用。Java的ThreadGrup 类提供了大量的方法来方便我们对线程组树中的每一个线程组以及线程组中的每一个线程进行操作。 

线程组和线程池的区别:线程组和线程池是两个不同的概念,他们的作用完全不同,前者是为了方便线程的管理,后者是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。

1.3 线程本地变量ThreadLocal

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。从线程的角度看,目标变量就像是线程的本地变量,这也是类名中Local所要表达的意思。ThreadLocal的实现原理: 利用线程本身的threadLocalMap<ThreadLocal<?>,Object>存储变量副本,threadLocal<?>作为查询的键,参考源码如下:

ThreadLocalMap的结构如下:

注意事项:使用 ThreadLocal ,一般都是声明在静态变量中,如果不断地创建ThreadLocal 而且没有调用其 remove 方法,将会导致内存泄露,特别是在高并发的 Web 容器当中这么做的时候。

1.4 线程的异常处理

run方法不允许 throw exception,所有的异常必须在 run 方法内进行处理。在 Java 多线程程序中,所有线程都不允许抛出未捕获的checkedexception,也就是说各个线程需要自己把自己的 checked exception 处理掉,这一点是通过java.lang.Rumnnable.run方法声明进行了约束,因为此方法声明上没有throwexception部分。但是线程依然有可能抛出uncheckedexception,当抛出此类异常时,线程就会终结,而对于主线程和其他线程完全不受影响,且完全感知不到某个线程抛出的异常,也是说完全无法 catch 到这个异常。

JVM 的这种设计源自于这样一种理念:“线程是独立执行的代码片断,线程的问题应该由线程自己来解决,而不要委托到外部。”基于这样的设计理念,在Java中,线程方法的异常,无论是checked还是unchecked exception,都应该在线程执行的run方法内catch 并处理掉。因此,在thread里面,如果要处理checked exception,简单的一个ty/catch 块就可以了。对于这种 unchecked exception,相对来说就会有点不一样。这时就要用到 Thread 里面的 setUncaughtExceptionHandler(UncaughtExceptionHandler),这个方法可以用来处理一些unchecked exception。setUncaughtExceptionHandler方法相当于一个事件注册的入口。在 Thread 类里面的源码如下:

UncaughtExceptionHandler源码如下:

当UncaughtExceptionHandler指定的处理异常的方法发生异常时,JVM将忽略此方法引发的任何异常。

1.5 线程通信

(1)sleep()

sleep方法是让出CPU资源,不会释放锁资源。作用是:保持对象锁,让出CPU,不让当前线程霸占进程获取的CPU资源,以留一定时间给其他线程执行。

(2)yield()

yield方法不会释放当前线程持有的任何锁资源,它只会改变线程的执行状态,从执行状态转变为就绪状态,以便其他线程有机会执行。Thread.yield() 方法通常是建议调度器让出当前线程的 CPU 时间片,以便让其他具有相同或更高优先级的线程运行。这意味着如果有其他具有相同或更高优先级的线程处于就绪状态,调度器会有更大的倾向性选择这些线程来运行,而不是当前调用了 Thread.yield() 的线程。因此,它会考虑其他线程的优先级,但不是绝对保证。调度器的具体行为还受到操作系统和 Java 虚拟机的实现策略的影响。

(3)join()

在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。

join源码中,没看到有调用notify() 或者 notifyAll() 唤醒方法。在java中,Thread类线程执行完run()方法后,会自动执行notifyAll()方法。因为线程在die的时候会释放持用的资源和锁,自动调用自身的notifyAll方法。这个细节在Java的Native方法中,观察C/C++源码:

(4)wait() 、notify() 、notifyAll()

wait方法使当前线程从调用处中断并且释放锁转入等待队列,直到收到notify或者notifyAll的通知才能从等待队列转入锁池队列。如果没有收到停止通知,线程会一直等待,除非线程被中断或调用了带超时的wait(long timeout)方法。注意事项如下:

①wait方法必须在同步代码块或同步方法中调用,否则,会抛出IllegalMonitorStateException异常。

②wait方法会释放当前线程所持有的锁,使得其他线程有机会进入同步代码块或同步方法。

③wait方法可以被中断,如果在等待过程中线程被中断,会抛出InterruptedException异常。

随机从等待队列中通知一个持有相同锁的一个线程。如果没有持有相同锁的wait线程,那么该通知会被忽略。

使用场景:当线程需要等待某个条件成立时,可以使用wait方法将线程挂起,等待其他线程的通知。

notify方法随机从等待队列中通知一个持有相同锁的一个线程。如果没有持有相同锁的wait线程,那么该通知会被忽略。注意事项如下:

①notify方法也必须在同步代码块或同步方法中调用。

②调用notify方法的线程不会立即释放锁,而是会继续执行同步代码块或同步方法中的剩余代码,直到执行完毕并退出同步块后,才会释放锁。此时,被唤醒的线程会尝试重新获取锁,以继续执行。

③由于是随机唤醒一个线程,因此在使用时需要谨慎,避免造成线程假死等问题。

使用场景:当某个条件已经满足,并且只需要唤醒一个等待该条件的线程时,可以使用notify方法。

notifyAll方法通知等待队列中持有相同锁的所有线程,让它们转入锁池队列。如果没有持有相同锁的wait线程,那么该通知会被忽略。注意事项和notify类似。示例代码如下:

(5)await()、 signal() 、signalAll()

Condition接口定义的await、signal、signalAll方法使用与Object提供的wait、notify、notifyAll类似。condition可以理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。示例如下:

本章节内容到此为止!

标签:调用,第三章,编程,并发,线程,notify,方法,守护,wait
From: https://blog.csdn.net/pig_Struggle/article/details/140161025

相关文章

  • 玄机——第三章 权限维持-linux权限维持-隐藏 wp
    文章目录一、前言二、概览简介三、参考文章四、步骤(解析)准备步骤#1.0步骤#1.1黑客隐藏的隐藏的文件完整路径md5步骤#1.2黑客隐藏的文件反弹shell的ip+端口{ip:port}步骤#1.3黑客提权所用的命令完整路径的md5flag{md5}拓展1.1拓展1.2步骤#1.4黑客尝试注入恶意代......
  • 【重走编程路】设计模式概述(三) -- 单例模式
    文章目录前言设计模式详解3.单例模式(Singleton)问题解决方案应用场景实现代码1.懒汉式单例模式2.加锁的懒汉式单例模式3.饿汉式单例模式4.静态内部变量(c++11)5.call_once实现懒汉单例前言创建型模式主要关注对象的创建过程,提供了一种创建对象的最佳方式,并隐......
  • Java编程从入门到放弃
    1.配置开发环境安装JDK官网下载地址:https://www.oracle.com/java/technologies/downloads/配置环境变量最新版本JDK22无需手动配置环境变量。老版本:此电脑-右键属性-高级系统设置-环境变量-系统变量-Path-编辑C:\Java\jdk1.8.0_65\bin检查结果java-versionHelloWor......
  • Golang开发:构建支持并发的网络爬虫
    Golang开发:构建支持并发的网络爬虫随着互联网的快速发展,获取网络数据成为了许多应用场景中的关键需求。网络爬虫作为一种自动化获取网络数据的工具,也因此迅速崛起。而为了应对日益庞大的网络数据,开发支持并发的爬虫成为了必要的选择。本文将介绍如何使用Golang编写一个支持......
  • C#面:编程长度为10000的字符串,通过随机从a-z中抽取10000个字符组成
    使用C#中的Random类来生成随机字符,并使用StringBuilder类来构建字符串。以下是一个示例程序:usingSystem;usingSystem.Text;classProgram{staticvoidMain(){Randomrandom=newRandom();StringBuilderstringBuilder=newStringBuild......
  • Java 流式编程详解,Demo案例解析
    Java流式编程详解,Demo案例解析JavaStreams在很多年前就被引入了,但作为Java开发者,我们还没有完全掌握这个多功能工具的威力。在这里,你将发现一些有价值的技巧,可以作为参考并应用到你的下一个项目中。在下面的示例中,我们将使用以下类。@GetterclassCompany{privat......
  • 思考如何学习一门编程语言?
    一、什么是编程语言编程语言是一种用于编写计算机程序的人工语言。通过编程语言,程序员可以向计算机发出指令,控制计算机执行各种任务和操作。编程语言由一组语法规则和语义规则组成,这些规则定义了如何编写代码以及代码的含义。编程语言的基本组成部分语法(Syntax):语法......
  • 基于gunicorn+flask+docker模型高并发部署
    为了基于Gunicorn、Flask和Docker构建一个高并发的Web应用部署环境,我们需要进行以下几个步骤:编写Flask应用:创建一个简单的Flask应用。配置Gunicorn:使用Gunicorn来管理多个工作进程,提高并发处理能力。创建Docker镜像:编写Dockerfile来创建一个Docker镜像。编写DockerCompose......
  • 并发编程 interrupt打断park
    视频常见方法-interrupt-打断park park线程是什么?在Java中,"park线程"通常指的是Java并发包(java.util.concurrent包)中的LockSupport类的相关方法,特别是park()和unpark(Threadthread)方法。LockSupport类提供了线程阻塞和解除阻塞的功能,这些功能不依赖于任何对象,而是直......
  • 【CUDA】 由GPGPU控制核心架构考虑CUDA编程中线程块的分配
    GPGPU架构特点由于典型的GPGPU只有小的流缓存,因此一个存储器和纹理读取请求通常需要经历全局存储器的访问延迟加上互连和缓冲延迟,可能高达数百个时钟周期。与CPU通过巨大的工作集缓存而降低延迟不同,GPU硬件多线程提供了数以千计的并行独立线程,这些线程可以在一个多处理器内部......