第12章 并 发

news2025/7/11 13:03:46

多进程与多线程本质的区别:每个进程都拥有自己的一整套变量,而线程则共享数据。

12.1 什么是线程

  1. 将执行这个任务的代码放在一个类的run方法中,这个类要实现Runnable接口。
    Runnable接口非常简单,只有一个run方法:
public interface Runnable
{
void run();
}
  1. 从这个Runnable构造一个Thread对象:
var t = new Thread(r);
  1. 启动线程:
t.start();   //start在新线程调用run方法

调用Thread.start方法,这会创建一个执行run方法的新线程。不要调用Thread类或Runnable对象的run方法。直接调用run方法只会在同一个线程中执行这个任务——而没有启动新的线程。

12.2 线程状态

抢占式调度系统 :给每一个可运行线程一个时间片来执行任务。当时间片用完时,操作系统剥夺该线程的运行权,并给另一个线程一个机会来运行。
现在所有的桌面以及服务器操作系统都使用抢占式调度。但是,像手机这样的小型设备可能使用协作式调度。在这样的设备中,一个线程只有在调用yield方法或者被阻塞或等待时才失去控制权。

12.2.1 新建线程

用new操作符创建一个新线程时,如newThread®,这个线程还没有开始运行。这意味
着它的状态是新建

12.2.2 可运行线程

一旦调用start方法,线程就处于可运行(runnable)状态。一个可运行的线程可能正在
运行
也可能没有运行。要由操作系统为线程提供具体的运行时间。
在有多个处理器的机器上,每一个处理器运行一个线程,可以有多个线程并行运行。当
然,如果线程的数目多于处理器的数目,调度器还是需要分配时间片

12.2.3 阻塞和等待线程

要由线程调度器重新激活这个线程。具体细节取决于它是怎样到达非活动状态的。

  • 当一个线程试图获取一个内部的对象,这个锁目前被其他线程占有,该线程就会被阻塞。当所有其他线程都释放了这个锁,并且线程调度器允许该线程持有这个锁时,它将变成非阻塞状态。
  • 当线程等待另一个线程通知调度器出现一个条件时,这个线程会进人等待状态。
  • 有几个方法有超时参数,调用这些方法会让线程进人计时等待状态。这一状态将一直保持到超时期满或者接收到适当的通知。带有超时参数的方法有Thread.sleep 和计时版的 Object.wait, Thread.join, Lock.tryLock 以及 Condition.await。
    在这里插入图片描述
    当一个线程阻塞或等待时(或终止时),可以调度另一个线程运行。当一个线程被重新激活(例如,因为超时期满或成功地获得了一个锁),调度器检查它是否具有比当前运行线程更高的优先级。如果是这样,调度器会剥夺某个当前运行线程的运行权,选择一个新线程运行。

12.2.4 终止线程

线程会由于以下两个原因之一而终止:

  • run方法正常退出,线程自然终止。
  • 因为一个没有捕获的异常终止了 run方法,使线程意外终止。

12.3线程属性

12.3.1 中断线程

  1. 除了已经废弃的stop方法,没有办法可以强制线程终止。不过,interrupt方法可以用来请求终止一个线程。

  2. 要想得出是否设置了中断状态,首先调用静态的Thread. currentThread方法获得当前线程,然后调用islnterrupted方法:

while (! Thread. currentT read (). islnterrupted () && more work to do)
{
do more work
}
  1. 如果线程被阻塞,就无法检查中断状态。这里就要引人InterruptedException异常。
    当在一个被sleep即或wait调用阻塞的线程上调用interrupt方法时,那个阻塞调用将被一个InterruptedException异常中断。

  2. **没有任何语言要求被中断的线程应当终止。**中断一个线程只是要引起它的注意。被中断的线程可以决定如何响应中断。某些线程非常重要,所以应该处理这个异常,然后再继续执行。但是,**更普遍的情况是,线程只希望将中断解释为一个终止请求。**这种线程的run方法
    具有如下形式:

Runnable r = () -> {
try
{
while (!Thread.currentThread().islnterrupted() && more work to do)
{
do more work
}
}
catch(InterruptedException e)
{
// thread was interrupted during sleep or wait
}
finally
{
cleanup, if required
}
II exiting the run method terminates the thread
}
  1. 如果在每次工作迭代之后都调用sleep方法(或者其他可中断方法),islnterrupted检查既没有必要也没有用处。如果设置了中断状态,此时倘若调用sleep方法,它不会休眠。实际上,它会清除中断状态(!)并抛出InterruptedException。因此,如果你的循环调用了sleep,不要检测中断状态,而应当捕获InterruptedException异常,如下所示:
    Runnable r = () -> {
        try {
            while (more work to do) {
                //do more work
                Thread.sleep(delay);
            }
        } catch (InterruptedException e) {
             // thread was interrupted during sleep
        } finally {
            //cleanup, if required
        }
           // exiting the run method terminates the thread
    };

6.在这里插入图片描述

12.3.2 守护线程

  1. 守护线程的唯一用途是为其他线程提供服务。计时器线程就是一个例子,它定时地发送“计时器嘀嗒”信号给其他线程,另外清空过时缓存项的线程也是守护线程。
  2. 当只剩下守护线程时,虚拟机就会退出。因为如果只剩下守护线程,就没必要继续运行程序了。
t.setDaemon(true);

12.3.3 线程名

用setName方法为线程设置任何名字。这在线程转储时可能很有用。
var t = new Thread(runnable);
t.setName(“Web crawler”);

12.3.4 未捕获异常的处理器

在这里插入图片描述

12.4 同步

12.4.1 竞态条件的一个例子

在这里插入图片描述

12.4.2竞态条件详解

在这里插入图片描述

12.4.3 锁对象(重要)

两种机制可防止并发访问代码块:

  1. Java语言提供了一个synchronized关键字,会自动提供一个锁以及相关的“条件”
  2. 另外Java5引人了ReentrantLock类。用ReentrantLock保护代码块的基本结构如下:

      myLock.lock(); // a ReentrantLock object
      try
      {
         critical section
      }
      finally
      {
         myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
      }

这个结构确保任何时刻只有一个线程进人临界区。一旦一个线程锁定了锁对象,其他任何线程都无法通过lock语句。当其他线程调用lock时,它们会暂停,直到第一个线程释放这个锁对象。
在这里插入图片描述

  1. ReentrantLock的例子
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

12.4.4 条件对象(重要)

通常,线程进人临界区后却发现只有满足了某个条件之后它才能执行。可以使用一个
条件变量来管理那些已经获得了一个锁却不能做有用工作的线程。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

12.4.5 synchronized关键字(重要)

BrianGoetz创造了以下“同步格言”:“如果写一个变量,而这个变量接下来可能会被另一个线程读取,或者,如果读一个变量,而这个变量可能已经被另一个线程写入值,那么必须使用同步。”
在这里插入图片描述
在这里插入图片描述

12.4.6 同步块

  1. 线程可以通过调用同步方法获得锁。还有另一种机制可以获得锁:即进人一个同步块。
  2. 客户端锁定是非常脆弱的,非常不推荐使用
    在这里插入图片描述

12.4.7监视器概念

Java的synchronized不太严格的实现了监视器。学习synchronized即可。

  1. 显式锁严格地讲,不是面向对象的。希望不要求程序员考虑显式锁就可以保证多线程的安全性,由此产生监视器。
  2. 监视器具有如下特性:
  • 监视器是只包含私有字段的
  • 监视器类的每个对象一个关联的锁
  • 所有方法由这个锁锁定。换句话说,如果客户端调用obj.method(),那么obj对象的锁在方法调用开始时自动获得,并且当方法返回时自动释放该锁。因为所有的字段是私有的,这样的安排可以确保一个线程处理字段时,没有其他线程能够访问这些字段。
  • 锁可以有任意多个相关联的条件
  1. Java设计者以不太严格的方式采用了监视器概念,Java中的每一个对象都有一个内部锁和一个内部条件。如果一个方法用synchronized关键字声明,那么,它表现得就像是一个监视器方法。可以通过调用wait/notifyAll/notify来访问条件变量。

  2. Java对象在以下3个重要方面不同于监视器,这削弱了线程的安全性:

  • 字段不要求是private。
  • 方法不要求是synchronized。
  • 内部锁对客户是可用的。

12.4.8 volatile 字段

  1. volatile关键字为实例字段的同步访问提供了一种免锁机制。如果声明一个字段为volatile,那么编译器和虚拟机就知道该字段可能被另一个线程并发更新。

在这里插入图片描述

12.4.9 final 变量

多个线程安全地读取一个字段:

  1. volatile变量
  2. final变量。
    在这里插入图片描述

final保证accounts这个对象引用不会变,不可能指向另一个对象。但是,它的内容可能变。所以final变量是同步的,但映射的操作不是线程安全的。

12.4.10 原子性

见书

12.4.11 死锁

Java编程语言中没有任何东西可以避免或打破这种死锁。必须仔细设计程序,确保不会出现死锁。

12.4.12 线程局部变量

  1. 要为每个线程构造一个实例,可以使用以下代码:
public static final ThreadLocal\<SimpleDateFormat> dateFormat =ThreadLocal.withlnitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
  1. 要访问具体的格式化方法,可以调用:
String dateStamp = dateFormat.get().format(new DateO);
  1. 应用场景:线程不安全的对象、多个线程等待共享同一个对象低效。
  • 例如,SimpleDateFormat类不是线程安全的。假设有一个静态变量:public static final SimpleDateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");如果两个线程都执行以下操作:StringdateStamp =d ateFormat.format(newDateO);结果可能很混乱,因为dateFormat使用的内部数据结构可能会被并发的访问所破坏。当然可以使用同步,但开销很大;或者也可以在需要时构造一个局部SimpleDateFormat对象,不过这也太浪费了。
  • 在多个线程中生成随机数也存在类似的问题。java.util.Random类是线程安全的,但是如果多个线程需要等待一个共享的随机数生成器,这会很低效
    可以使用ThreadLoacl辅助类为各个线程提供一个单独的生成器,不过Java7还另外提供了一个便利类。只需要做以下调用:int random = ThreadLocalRandom.current().nextlnt(upperBound); ThreadLocalRandom.current()调用会返冋特定于当前线程的Random类的实例。

12.4.13 为什么废弃stop和suspend方法

  1. stop方法是不安全的,该方法会终止所有未结束的方法,包括run方法。当线程被终止,它会立即释放被它锁定的所有对象的。这会导致对象处于不一致的状态。

假设一个Transfer在从一个账户向另一个账户转账的过程中被终止,钱已经取出,但还没有存人目标账户,现在银行对象就被破坏了。因为锁已经被释放,其他未停止的线程也会观察到这种破坏。

  1. 当一个线程要终止另一个线程时,它无法知道什么时候调用stop方法是安全的,而什么时候会导致对象被破坏。因此,该方法已经被废弃。希望停止一个线程的时候应该中断该线程,被中断的线程可以在安全的时候终止。

  2. suspend会导致死锁。与stop不同,suspend不会破坏对象。但是,如果用suspend挂起一个持有锁的线程,那么,在线程恢复运行之前这个锁是不可用的。如果调用suspend方法的线程试图获得同一个锁,那么程序死锁:被挂起的线程等着被恢复,而将其挂起的线程等待获得锁。

12.5 线程安全的集合

12.5.1 阻塞队列

  1. 生产者线程向队列插人元素,消费者线程则获取元素。使用队列,可以安全地从一个线程向另一个线程传递数据。

考虑银行转账程序,转账线程将转账指令对象插人一个队列,而不是直接访问银行对象。另一个线程从队列中取出指令完成转账。只有这个线程可以访问银行对象的内部.因此不需要同步。

  1. 当试图向队列添加元素而队列已满,或是想从队列移出元素而队列为空的时候,阻塞队 列将导致线程阻塞。
  2. 队列会自动地平衡负载。

如果第一组线程运行得比第二组慢,第二组在等待结果时会阻塞。如果第一组线程运行得更快,队列会填满,直到第二组赶上来。

  1. 当试图向满队列添加元素或者想从空队列得到队头元素时,add、remove和element操作会抛出异常。当然,在一个多线程程序中,队列可能会在任何时候变空或变满,因此,应当使用offer、poll和peek方法作为替代。如果不能完成任务,这些方法只是给出一个错误提示而不会抛出异常。

poll和peek方法返回null来指示失败。因此,向这些队列中插入null值是非法的。

  1. 如果使用队列作为线程管理工具,将要用到put和take方法。如果队列满,put方法阻塞;如果队列空,则take方法阻塞。它们与不带超时参数的offer和poll方法等效。
  2. 还有带有超时时间的offer方法和poll方法。

boolean success = q.offer(x, 100, Timellnit.MILLISECONDS);尝试在100毫秒的时间内在队尾插入一个元素。如果成功返回true;否则,如果超时,则返回false。

  1. java.util.concurrent包提供了阻塞队列的几个变体。在这里插入图片描述

理解:

  • 生产者和消费者只在队满继续加元素或者队空要删除元素时,需要线程间通信。其它时间,互不干扰。
  • add方法没有同步锁。offer和put方法有锁。add方法不是线程安全的。
  • put方法采用条件变量实现了阻塞。offer方法不阻塞,返回false。

12.5.2 高效的映射、集和队列

个人理解:线程安全定义

线程安全的主题是对象。线程安全定义:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。深入理解Java虚拟机》的作者也认可这个观点。本人也认为这是一个恰当的定义,因为线程安全的主体是什么?是方法还是代码块?这里给出的主体是对象,这是非常恰当的,因为Java是纯面向对象的,Java中一切为对象。因此通过对象定义线程安全是恰当的。

但是,这里并不是说其他的方式定义不对(这里绝没有这个意思)。我们可以看一下其他的定义方式,进行一下对比:
————————————————
版权声明:本文为CSDN博主「凡尘炼心」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:

12.5.3 映射条目的原子更新在这里插入图片描述

12.5.4 对并发散列映射的批操作

12.5.5 并发集视图

12.5.6 写数组的拷贝

12.5.7 并行数组算法

12.5.8 较早的线程安全集合

在这里插入图片描述

12.7 异步计算

12.7.1 可完成Future

12.7.2 组合可完成Future

12.7.3 用户界面回调中的长时间运行任务

12.8 进程

12.8.1 建立一个进程

12.8.2 运行一个进程

12.8.3 进程句柄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/618787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

情感分析实战(中文)-数据获取

情感分析实战(中文)-数据获取 背景&#xff1a;该专栏的目的是将自己做了N个情感分析的毕业设计的一个总结版&#xff0c;不仅自己可以在这次总结中&#xff0c;把自己过往的一些经验进行归纳&#xff0c;梳理&#xff0c;巩固自己的知识从而进一步提升&#xff0c;而帮助各大…

Ubuntu20、centos7安装部署Gitlab

目录 一、简介 二、安装GitLab 1、安装准备工作 2、安装Gitlab a、安装并配置必要的依赖 b、下载Gitlab c、启动postfix邮件服务&#xff0c;设置开机自启 d、安装Gitlab e、修改gitlab配置文件&#xff08;ip和端口&#xff09; g、更新配置文件并重启 h、通过ip地址加端口进行…

国产力作:全新Excel平台,画表格搭建软件,Access用户:告别VBA

全新Excel平台&#xff0c;功能强大到离谱&#xff1f; 最近&#xff0c;发现了一款新型的软件&#xff0c;而且还是国产的&#xff0c;功能超级强大&#xff0c;用法却很简单。就是感觉非常的厉害&#xff01; 一款全新的Excel平台&#xff0c;但是却跟Excel没有任何联系&am…

webpack.config.js基础配置(五大核心属性)

在上一节webpack零基础入门中我们在安装完webpack 和 webpack-cli依赖之后&#xff0c;直接通过npx webpack ./src/main.js --modedevelopment的方式对src下的js文件进行了打包。 其中的 ./src/main.js: 指定 Webpack 从 main.js 文件开始打包&#xff0c;不但会打包 main.js&a…

A ConvNet for the 2020s

A ConvNet for the 2020s 2020年代的ConvNet https://openaccess.thecvf.com/content/CVPR2022/papers/Liu_A_ConvNet_for_the_2020s_CVPR_2022_paper.pdf Zhuang Liu 1 , 2 ∗ ^{1,2*} 1,2∗ Hanzi Mao 1 ^1 1 Chao-Yuan Wu 1 ^1 1 Christoph Feichtenhofer 1 ^1 1 Trevor Da…

mpls vpn综合实例配置案例

如图1所示&#xff1a; 1、AR4连接公司总部财务部、AR6连接分支机构财务部&#xff0c;AR4和AR6属于vpna&#xff1b; 2、 AR5连接公司总部办公、 AR7连接分支机构办公&#xff0c; AR5和 AR7属于vpnb。 公司要求通过部署BGP/MPLS IP VPN&#xff0c;实现总部和分支机构的安全互…

2023年6月DAMA-CDGA/CDGP数据治理工程师认证到这家

DAMA认证为数据管理专业人士提供职业目标晋升规划&#xff0c;彰显了职业发展里程碑及发展阶梯定义&#xff0c;帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力&#xff0c;促进开展工作实践应用及实际问题解决&#xff0c;形成企业所需的新数字经济下的核心职业…

CleanMyMac X 4.13.4许可证激活码2023最新免费版

小伙伴们&#xff0c;你们好&#xff0c;今天兔八哥爱分享来聊聊cleanmymac X如何激活&#xff0c;关于cleanmymac的基本情况说明介绍的文章,网友们对这件事情都比较关注&#xff0c;那么现在就为大家来简单介绍下&#xff0c;希望对各位小伙伴们有所帮助。在不断更新的版本中&…

数字化转型的难点是什么?该如何突破?

01为什么要进行数字化转型&#xff1f; 数字化转型不仅是企业提高效率和竞争力的必经之路&#xff0c;也是市场发展趋势的体现。 提升业务效率&#xff1a;数字化转型可以采用自动化流程、数据分析和智能化技术&#xff0c;从而提高企业业务的自动化水平&#xff0c;优化流程…

TLE7244SL-ASEMI代理英飞原装汽车芯片TLE7244SL

编辑&#xff1a;ll TLE7244SL-ASEMI代理英飞原装汽车芯片TLE7244SL 型号&#xff1a;TLE7244SL 品牌&#xff1a;Infineon(英飞凌) 封装&#xff1a;SSOP-24-150mil 类型&#xff1a;电源负载开关 TLE7244SL特性 4个输入引脚&#xff0c;提供灵活的PWM配置 由专用引脚…

XML转换成JSON

说在前面 相信大家对 XML 都不会很陌生了&#xff0c;XML 被设计用来结构化、存储以及传输信息。最近在开发过程中发现&#xff0c;有一些旧接口返回的数据格式即是 XML 的格式&#xff0c;因此需要我们对返回的 XML 数据进行解析&#xff0c;转换成我们好处理的 JSON 数据结构…

SCI论文去哪里查找下载

SCI论文&#xff0c;是指被SCI(Scientific Citation Index)&#xff0c;即科学引文索引所收录的SCI期刊上刊登的学术期刊论文。如何查找下载SCI论文呢&#xff1f;请看下面讲解。 Web of Science数据库 Web of Science是获取全球学术信息的重要数据库&#xff0c;它收录了全球…

vue篇——vue原理以及实现

vue 官网解释Vue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架&#xff0c;简单易学、双向数据绑定、组件化。数据和结构的分离、虚拟DOM、运行速度快。链接 它有以下的特性&#xff1a;1.轻量级的框架&#xff1b;2.双向数据绑定&#xff1b;3…

MySQL索引事务(一)

1、索引 1.1、概念 索引相当于一种特殊文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c;并指定索引类型&#xff0c;各类索引各自的结构实现。 1.2、作用 *通俗来讲&#xff0c;索引就相当于是我们的书本目录&#xff0c;…

电视、报纸、杂志、网络等曝光度好的媒体有哪些?

在当今社会&#xff0c;媒体是传递信息、宣传思想和引导社会舆论的重要平台。对于企业和个人而言&#xff0c;选择曝光度好的媒体来传递信息&#xff0c;是提高知名度和影响力的重要手段。那么&#xff0c;曝光度好的媒体有哪些呢&#xff1f;下面将从电视、报纸、杂志、网络等…

chatgpt赋能python:Python语言在SEO中的应用

Python语言在SEO中的应用 介绍 Python是一种易于学习&#xff0c;功能强大的高级编程语言&#xff0c;其优秀的性能和简单易用的开发方式&#xff0c;使其成为当前最受欢迎的语言之一。Python在各种应用场景中都有着广泛的应用&#xff0c;其中SEO领域更是不可忽视。Python可…

Tuxera for Mac2023中文版电脑读写硬盘U盘工具

在我们的日常学习和生活当中&#xff0c;因为小巧、便于携带等特性&#xff0c;U盘成为比较常用的移动存储设备。但在使用的过程中&#xff0c;也经常会遇到一些棘手的问题&#xff0c;例如&#xff1a;插入之后无法识别&#xff0c;或只能查看不能读写。那么&#xff0c;U盘不…

工欲善其事,必先利其器还是有必要的

uPic 图床配置教程 - Github 背景 最近发现上传图片的图床服务越来越难用&#xff0c;有的时候上传很慢&#xff0c;甚至会上传不上去。所以搜罗一波&#xff0c;本地实战搞起&#xff0c;巧了&#xff0c;这个软件不错&#xff0c;能达到起码得要求&#xff0c;正适合博客上…

MyBatis-Plus 可视化代码生成器来啦,开发效率提升2倍

前言 一、mybatis-plus-generator-ui是什么&#xff1f; 二、 mybatis-plus-generator-ui怎么用&#xff1f; 1、maven pom引入 2、新建程序入口&#xff0c;以main函数的方式运行 3、实例运行 三、mybatis-plus-generator-ui代码生成 1、Table的查询和浏览 2、输出配置 …

chatgpt赋能python:Python如何调字体大小的方法和步骤

Python如何调字体大小的方法和步骤 什么是调字体大小&#xff1f; 调字体大小是一种常见的文本编辑功能&#xff0c;可以通过更改文本中字号的大小&#xff0c;调整文本在屏幕上显示的大小。在Python编程中&#xff0c;也可以通过调整字体大小来改善程序界面的可读性。 如何…