Java 并发编程(四):保证对象的线程安全性

2023-05-16

02、线程安全类

设计一个线程安全类需要三个步骤:

1)找出表示对象状态的所有变量 2)对变量进行有效性约束 3)增加类的并发访问策略

我在作者说的基础上做了微调,读起来更加容易理解。怎么和代码对应起来了,先来看一个普通的计数器类 Counter。

public class Counter {
    private int value = 0;

    public int getValue() {
        return value;
    }

    public int increment() {
        return ++value;
    }
}

1)Counter 的状态变量只有一个,就是 value。

2)value 的有效性是什么呢,它最大不能超过 ` Integer.MAX_VALUE,最小只能为 0(计数嘛,总不能记成负数)。换句话说就是,value 的有效范围是 0 ~  Integer.MAX_VALUE`。

public int increment() {
    if (value == Integer.MAX_VALUE) {
        throw new IllegalStateException("counter overflow");
    }
    return ++value;
}

3)增加类的并发访问策略,直接上 synchronized。

public class Counter {
    private int value = 0;

    public synchronized int getValue() {
        return value;
    }

    public synchronized int increment() {
        if (value == Integer.MAX_VALUE) {
            throw new IllegalStateException("counter overflow");
        }
        return ++value;
    }
}

03、非线程安全的对象

之前我们谈了如何设计一个线程安全的类。如果类是安全的,那么它作为对象使用的时候就是线程安全的。但如果一个类不是线程安全的,它作为对象使用的时候怎么保证是线程安全的呢?

作者提到了一个名词叫做“封闭机制”:

1)把对象作为类的私有成员变量; 2)把对象作为方法内部的局部变量; 3)线程 A 把对象传递到 B 线程,而不是与线程 B 共享这个对象;

大家来看下面这段代码。

class StringList {
	private List<String> myList = new ArrayList<>();
	
	public synchronized void addString(String s) {
		myList.add(s);
	}
	
	public synchronized void removeString(String s) {
		myList.remove(s);
	}
}

本身 ArrayList 不是线程安全的,但 myList 是私有的,访问它的两个方法 addString() 和 removeString() 都加了关键字 synchronized,因此 myList 在使用的时候就变成了线程安全的对象,StringList 类就变成了一个线程安全的类——这种方式被称作 Java 监视器模式:可变的状态被封装在一个类中,访问它们只能通过加上锁的方法。

查看 Vector 的源码,你会发现,它之所以是线程安全的,就是采用的这种监视器模式

04、在已有的线程安全类上追加功能

假如现在有一个线程安全的类,比如之前提到的 StringList,它包含了大多数我们需要的功能,但还不够,那么怎么确保我们追加的功能不破坏原有的线程安全性呢?

最直接的方法当然是修改源码,假如源码掌握在我们自己手里的话。

class StringList {
	private List<String> myList = new ArrayList<>();
	
	public synchronized void addString(String s) {
		myList.add(s);
	}
	
	public synchronized void addIfNotExist(String s) {
		boolean isExist = myList.contains(s);
		if (!isExist) {
			myList.add(s);
		}
	}
}

我们新增了一个 addIfNotExist() 方法:如果字符串 s 还没有添加到 List 当中,就添加一个。

新增的方法没有破坏 StringList 的线程安全性,因为当两个线程同时执行 addIfNotExist() 方法时,需要经过 synchronized 把守的这道大门。

但很多时候,我们无法直接修改源码,这时候就只好在原来的基础上进行改造。大家听过之前的“红芯”浏览器吗?在谷歌浏览器的内核上裹了一层层皇帝的新衣。

class StringList {
	protected List<String> myList = new ArrayList<>();
	
	public synchronized void addString(String s) {
		myList.add(s);
	}
}

public class NewStringList extends StringList {
	public synchronized void addIfNotExist(String s) {
		boolean isExist = myList.contains(s);
		if (!isExist) {
			myList.add(s);
		}
	}
}

新建一个类 NewStringList,继承自 StringList,然后在 NewStringList 中新增一个方法 addIfNotExist()。当然了,这样做的前提是父类中的 myList 是 protected 而不是 private 的。因此,这种做法不具有普适性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 并发编程(四):保证对象的线程安全性 的相关文章

  • RxJava2源码分析——Map操作符

    本文章用的RxJava和RxAndroid版本如下 xff1a implementation 39 io reactivex rxjava2 rxjava 2 2 6 39 implementation 39 io reactivex rx
  • 交叉编译pytorch的aarch64版本

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 前言一 基础环境二 编译流程1 下载源码并配置TOOLCHAIN FILE内容2 预编译出protoc库和sleef库3 ana
  • 使用CSplitterWnd类静态分割的窗口的隐藏[转]

    标题略长 之前百度了很多 xff0c 也看过了很多程序 xff0c 那个时候稍微有点小青涩 xff0c 所以那些东西根本是看不懂什么意思 现在回过头来看 xff0c 其实还是很容易就实现的 当然 xff0c 话题很初级 xff0c 不是面向
  • Rxjava2源码-FlatMap操作符

    先来看一下使用demo Observable create new ObservableOnSubscribe lt String gt 64 Override public void subscribe ObservableEmitter
  • 代理设计模式

    代理模式的结构与实现 代理模式的结构比较简单 xff0c 主要是通过定义一个继承抽象主题的代理来包含真实主题 xff0c 从而实现对真实主题的访问 xff0c 下面来分析其基本结构和实现方法 1 模式的结构 代理模式的主要角色如下 抽象主题
  • 适配器模式

    模式的结构与实现 类适配器模式可采用多重继承方式实现 xff0c 如 C 43 43 可定义一个适配器类来同时继承当前系统的业务接口和现有组件库中已经存在的组件接口 xff1b Java 不支持多继承 xff0c 但可以定义一个适配器类来实
  • 装饰器模式

    装饰器模式的结构与实现 通常情况下 xff0c 扩展一个类的功能会使用继承方式来实现 但继承具有静态特征 xff0c 耦合度高 xff0c 并且随着扩展功能的增多 xff0c 子类会很膨胀 如果使用组合关系来创建一个包装对象 xff08 即
  • 享元设计模式

    享元模式的结构与实现 享元模式的定义提出了两个要求 xff0c 细粒度和共享对象 因为要求细粒度 xff0c 所以不可避免地会使对象数量多且性质相近 xff0c 此时我们就将这些对象的信息分为两个部分 xff1a 内部状态和外部状态 内部状
  • 组合设计模式

    组合模式的结构与实现 组合模式的结构不是很复杂 xff0c 下面对它的结构和实现进行分析 1 模式的结构 组合模式包含以下主要角色 抽象构件 xff08 Component xff09 角色 xff1a 它的主要作用是为树叶构件和树枝构件声
  • 模板方法模式

    模式的结构与实现 模板方法模式需要注意抽象类与具体子类之间的协作 它用到了虚函数的多态性技术以及 不用调用我 xff0c 让我来调用你 的反向控制技术 现在来介绍它们的基本结构 1 模式的结构 模板方法模式包含以下主要角色 1 xff09
  • 策略设计模式

    策略模式的结构与实现 策略模式是准备一组算法 xff0c 并将这组算法封装到一系列的策略类里面 xff0c 作为一个抽象策略类的子类 策略模式的重心不是如何实现算法 xff0c 而是如何组织这些算法 xff0c 从而让程序结构更加灵活 xf
  • 命令设计模式

    命令模式的结构与实现 可以将系统中的相关操作抽象成命令 xff0c 使调用者与实现者相关分离 xff0c 其结构如下 1 模式的结构 命令模式包含以下主要角色 抽象命令类 xff08 Command xff09 角色 xff1a 声明执行命
  • 状态设计模式

    状态模式的结构与实现 状态模式把受环境改变的对象行为包装在不同的状态对象里 xff0c 其意图是让一个对象在其内部状态改变的时候 xff0c 其行为也随之改变 现在我们来分析其基本结构和实现方法 1 模式的结构 状态模式包含以下主要角色 环
  • linux开启关闭端口(iptables 无法使用情况下)

    一 查看端口开启状态 查询已开放的端口 netstat anp root 64 localhost etc firewall cmd query port 61 8080 tcp 提示 yes xff0c 表示开启 xff1b no表示未开
  • 中介者模式

    模式的结构与实现 中介者模式实现的关键是找出 中介者 xff0c 下面对它的结构和实现进行分析 1 模式的结构 中介者模式包含以下主要角色 抽象中介者 xff08 Mediator xff09 角色 xff1a 它是中介者的接口 xff0c
  • YUV解析

    一般的视频采集芯片输出的码流一般都是 YUV 格式数据流 xff0c 后续视频处理也是对 YUV 数据流进行编码和解析 所以 xff0c 了解 YUV 数据流对做视频领域的人而言 xff0c 至关重要 在介绍 YUV 格式之前 xff0c
  • Android Camera旋转角度

    首先理解一下 info orientation 官方解释 官方定义 xff1a orientation 表示相机图像的方向 它的值是相机图像顺时针旋转到设备自然方向一致时的角度 例如假设设备是竖屏的 后置相机传感器是横屏安装的 当你面向屏幕
  • NV21 图像旋转处理 ( 后置摄像头顺时针旋转 90 度 | 前置摄像头顺时针旋转 90 度 )

    1 NV21 格式图像数据的排列 16 1616 个 Y 灰度数据在前 然后 4 44 组 8 88 个 VU 色彩值 饱和度 数据交替存放 2 NV21 格式的图像的 YUV 值顺时针旋转 90 度后的 YUV 矩阵为 3 灰度值 Y 数
  • enum 实现 Parcelable 接口

    enum 实现 Parcelable 接口 当你创建一个枚举 xff0c 想要使用上述插件时 xff0c 就会发现无法序列号 这个是因为 Parcel writeXXX 没有写入枚举的方法 xff0c 所以无法直接实现 Parcelable
  • Java暂停/挂起线程(suspend())和恢复线程(resume())

    暂停线程意味着此线程还可以恢复运行 在 Java 多线程中 xff0c 可以使用 suspend 方法暂停线程 xff0c 使用 resume 方法恢复线程的执行 suspend 与 resume 方法 本节通过一个案例来介绍 suspen

随机推荐

  • Java yieId()方法如何使用

    yieId 方法的作用是放弃当前的 CPU 资源 xff0c 将它让给其他的任务去占用 CPU 执行时间 但放弃的时间不确定 xff0c 有可能刚刚放弃 xff0c 马上又获得 CPU 时间片 例 1 创建一个线程实现从 1 开始 xff0
  • Gson源码解析

    Gson简介 Gson xff0c 就是帮助我们完成序列化和反序列化的工作的一个库 日常用法 UserInfo userInfo 61 getUserInfo Gson gson 61 new Gson String jsonStr 61
  • Git内部原理

    Git是怎么储存信息的 这里会用一个简单的例子让大家直观感受一下git是怎么储存信息的 首先我们先创建两个文件 git init echo 39 111 39 gt a txt echo 39 222 39 gt b txt git add
  • Java并发的AQS原理详解

    线程阻塞原语 Java 的线程阻塞和唤醒是通过 Unsafe 类的 park 和 unpark 方法做到的 public class Unsafe public native void park boolean isAbsolute lon
  • MySql前瞻,数据库管理技术的发展阶段

    文章目录 数据管理技术的3个发展阶段人工管理阶段文件系统阶段数据库系统阶段各个阶段背景及特点 数据管理技术的3个发展阶段 在目前阶段 xff0c 存储和管理数据都离不开数据库 当数据存储到数据库后 xff0c 数据库管理系统就会对这些数据进
  • git 拉取远程分支到本地

    1 把远程分支拉到本地 git fetch origin develop xff08 develop为远程仓库的分支名 xff09 2 在本地创建分支dev并切换到该分支 git checkout b dev 本地分支名称 origin d
  • 会话描述协议SDP

    什么是SDP SDP xff08 Session Description Protocol xff09 是一种通用的会话描述协议 xff0c 主要用来描述多媒体会话 xff0c 用途包括会话声明 会话邀请 会话初始化等 WebRTC主要在连
  • android studio引入本地外部项目的Module

    方法一 1 File gt New gt Import Module 2 Source directory 这里选择其它工程的module 点击Finish完成 方法二 1 File gt New gt New Module 或在工程上右键
  • git merge冲突解决

    1 git merge冲突了 xff0c 根据提示找到冲突的文件 xff0c 解决冲突 如果文件有冲突 xff0c 那么会有类似的标记 2 修改完之后 xff0c 执行git add 冲突文件名 3 git commit 注意 没有 m选项
  • Java并发之CAS原理分析

    CAS 底层原理 CAS 的思想很简单 xff1a 三个参数 xff0c 一个当前内存值 V 旧的预期值 A 即将更新的值 B xff0c 当且仅当预期值 A 和内存值 V 相同时 xff0c 将内存值修改为 B 并返回 true xff0
  • git 删除分支

    删除一个已被终止的分支 如果需要删除的分支不是当前正在打开的分支 xff0c 使用branch d直接删除 git branch d lt branch name gt 异常 error Cannot delete branch 39 xx
  • 异常 Caused by: java.lang.ClassNotFoundException: Didn‘t find class “...“on path: DexPathList

    解决方法 xff1a Android的项目目录里是有两个build文件夹的 xff0c 一个是 xff1a 项目目录 app build xff0c 另一个是 xff1a 项目目录 build Build gt Clean Project
  • java消息队列,业务应用场景概述

    1 异步处理 场景说明 xff1a 用户注册后 xff0c 需要发注册邮件和注册短信 传统的做法有两种 1 串行的方式 xff1b 2 并行方式 a 串行方式 xff1a 将注册信息写入数据库成功后 xff0c 发送注册邮件 xff0c 再
  • TCP三次握手,四次挥手

    三次握手 xff1a 第一次握手 SYN 61 1 seq 61 x xff0c 发送完毕后 xff0c 客户端进入 SYN SEND 状态 第二次握手 SYN 61 1 ACK 61 1 seq 61 y ACKnum 61 x 43 1
  • 206. 反转链表

    方法一 xff1a 迭代 假设链表为 1 2 3 xff0c 我们想要把它改成 1 2 3 在遍历链表时 xff0c 将当前节点的 next 指针改为指向前一个节点 由于节点没有引用其前一个节点 xff0c 因此必须事先存储其前一个节点 在
  • 启动不了docker怎么办?关于docker报错

    常常有时候电脑重启之后或者前一天正常关机第二天就启动不了的问题 xff1f 问题描述 从terminal启动ubuntu1804报错 参考的对象类型不支持尝试的操作 直接启动ubuntu1804也不行 解决方法 以左下角鼠标右键管理员身份打
  • Java 并发编程(一)

    多线程带来的风险 public class Unsafe private int chenmo public int add return chenmo 43 43 上面这段代码在单线程的环境中可以正确执行 xff0c 但在多线程的环境中则
  • Java并发编程(二):保证共享变量的原子性

    怎么让一个类在多线程的环境下是安全的 xff0c 有 3 条法则 xff0c 让我来告诉你 xff1a 1 不在线程之间共享状态变量 2 将状态变量改为不可变 3 访问状态变量时使用同步 那你可能要问 xff0c 状态变量是什么 xff1f
  • Java 并发编程(三):保证共享变量的可见性

    Java 内存模型 xff08 Java Memory Model xff0c 简称 JMM xff09 描述了 Java 程序中各种变量 xff08 线程之间的共享变量 xff09 的访问规则 xff0c 以及在 JVM 中将变量存储到内
  • Java 并发编程(四):保证对象的线程安全性

    02 线程安全类 设计一个线程安全类需要三个步骤 xff1a 1 xff09 找出表示对象状态的所有变量 2 xff09 对变量进行有效性约束 3 xff09 增加类的并发访问策略 我在作者说的基础上做了微调 xff0c 读起来更加容易理解