1、常见线程安全类

  • String

  • Integer

  • StringBuffer

  • Random

  • Vector

  • Hashtable

  • java.util.concurrent (JUC)包下的类

这里说它们是线程安全的是指,多个线程调用它们同一个实例的某个方法时,是线程安全的。

Hashtable table = new Hashtable();

new Thread(()->{
   table.put("key", "value1");
}).start();

new Thread(()->{
   table.put("key", "value2");
}).start();

注意:

  • 它们的每个方法是原子的

  • 但它们多个方法的组合不是原子的。

1.1 线程安全类方法的组合

分析下面代码是否线程安全?

Hashtable table = new Hashtable();
// 线程1,线程2
if( table.get("key") == null) {
   table.put("key", value);
}

结论是线程不安全

1.2 不可变类线程安全性

String、Integer 等都是不可变类,因为其内部的状态不可以改变,因此它们的方法都是线程安全的

或许有疑问,String 有 replace,substring 等方法【可以】改变值啊,那么这些方法又是如何保证线程安全的呢?

原因为,原值并没有被改变,而是创建了一个新值,其内部的状态没有改变,因此它们的方法都是线程安全的。

例如下面的代码也是同理。

public class Immutable{
 private int value = 0;

 public Immutable(int value){
   this.value = value;
}

 public int getValue(){
   return this.value;
}
}

如果想增加一个增加的方法呢?

public class Immutable{
 private int value = 0;

 public Immutable(int value){
   this.value = value;
}

 public int getValue(){
   return this.value;
}
 
 public Immutable add(int v){
   return new Immutable(this.value + v);
}  
}

2、线程安全实例分析

例1:

public class MyServlet extends HttpServlet {
   // 是否安全? 不安全
   Map<String,Object> map = new HashMap<>();
   // 是否安全? 安全
   String S1 = "...";
   // 是否安全? 安全
   final String S2 = "...";
   // 是否安全? 不安全
   Date D1 = new Date();
   // 是否安全? 不安全 原因属于可变类型
   final Date D2 = new Date();
   
   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       // 使用上述变量
  }
}

例2:

public class MyServlet extends HttpServlet {
   // 是否安全? 不安全
   private UserService userService = new UserServiceImpl();
   
   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       userService.update(...);
  }
}

public class UserServiceImpl implements UserService {
   // 记录调用次数
   private int count = 0;
   
   public void update() {
       // ...
       count++;
  }
}

例3:

@Aspect
@Component
public class MyAspect {
   // 是否安全? 不安全 修改为环绕通知即可解决
   private long start = 0L;
   
   @Before("execution(* *(..))")
   public void before() {
       start = System.nanoTime();
  }
   
   @After("execution(* *(..))")
   public void after() {
       long end = System.nanoTime();
       System.out.println("cost time:" + (end-start));
  }
}

例4:

public class MyServlet extends HttpServlet {
   // 是否安全 安全
   private UserService userService = new UserServiceImpl();
   
   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       userService.update(...);
  }
}

public class UserServiceImpl implements UserService {
   // 是否安全 安全 没有可更改的属性
   private UserDao userDao = new UserDaoImpl();
   
   public void update() {
       userDao.update();
  }
}

public class UserDaoImpl implements UserDao {
   public void update() {
       String sql = "update user set password = ? where username = ?";
       // 是否安全 安全
       try (Connection conn = DriverManager.getConnection("","","")){
           // ...
      } catch (Exception e) {
           // ...
      }
  }
}

例5:

public class MyServlet extends HttpServlet {
   // 是否安全 安全
   private UserService userService = new UserServiceImpl();
   
   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       userService.update(...);
  }
}

public class UserServiceImpl implements UserService {
   // 是否安全 安全
   private UserDao userDao = new UserDaoImpl();
   
   public void update() {
       userDao.update();
  }
}

public class UserDaoImpl implements UserDao {
   // 是否安全   不安全
   private Connection conn = null;
   public void update() throws SQLException {
       String sql = "update user set password = ? where username = ?";
       conn = DriverManager.getConnection("","","");
       // ...
       conn.close();
  }
}

例6:

public class MyServlet extends HttpServlet {
   // 是否安全 安全
   private UserService userService = new UserServiceImpl();
   
   public void doGet(HttpServletRequest request, HttpServletResponse response) {
       userService.update(...);
  }
}

public class UserServiceImpl implements UserService {    
   public void update() {
       UserDao userDao = new UserDaoImpl();
       userDao.update();
  }
}

public class UserDaoImpl implements UserDao {
   // 是否安全 不安全
   private Connection = null;
   public void update() throws SQLException {
       String sql = "update user set password = ? where username = ?";
       conn = DriverManager.getConnection("","","");
       // ...
       conn.close();
  }
}

例7:

public abstract class Test {
   
   public void bar() {
       // 是否安全 不安全 foo暴露出
       SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
       foo(sdf);
  }
   
   public abstract foo(SimpleDateFormat sdf);
   
   
   public static void main(String[] args) {
       new Test().bar();
  }
}

其中 foo 的行为是不确定的,可能导致不安全的发生,被称之为外星方法

public void foo(SimpleDateFormat sdf) {
   String dateStr = "1999-10-11 00:00:00";
   for (int i = 0; i < 20; i++) {
       new Thread(() -> {
           try {
               sdf.parse(dateStr);
          } catch (ParseException e) {
               e.printStackTrace();
          }
      }).start();
  }
}

Java并发(十八)----常见线程安全类及实例分析的更多相关文章

  1. JAVA并发-为现有的线程安全类添加原子方法

    JAVA中有许多线程安全的基础模块类,一般情况下,这些基础模块类能满足我们需要的所有操作,但更多时候,他们并不能满足我们所有的需要.此时,我们需要想办法在不破坏已有的线程安全类的基础上添加一个新的原子 ...

  2. Java并发编程系列-(2) 线程的并发工具类

    2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了 ...

  3. 《java面试十八式》--引子

    爪哇城中   “喂,你等等我啊”少女气喘吁吁的喊道   “大小姐,你可快点吧,报名马上就要结束了.”   这是爪哇城一年一度的大选比赛,被选上的人会留下来任职,享有名誉和金钱,所以大家都在积极准备. ...

  4. java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

    Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...

  5. Java并发(八)计算线程池最佳线程数

    目录 一.理论分析 二.实际应用 为了加快程序处理速度,我们会将问题分解成若干个并发执行的任务.并且创建线程池,将任务委派给线程池中的线程,以便使它们可以并发地执行.在高并发的情况下采用线程池,可以有 ...

  6. Java并发编程学习:线程安全与锁优化

    本文参考<深入理解java虚拟机第二版> 一.什么是线程安全? 这里我借<Java Concurrency In Practice>里面的话:当多个线程访问一个对象,如果不考虑 ...

  7. Java并发读书笔记:线程安全与互斥同步

    目录 导致线程不安全的原因 什么是线程安全 不可变 绝对线程安全 相对线程安全 线程兼容 线程对立 互斥同步实现线程安全 synchronized内置锁 锁即对象 是否要释放锁 实现原理 啥是重进入? ...

  8. java并发编程实战之线程安全性(一)

    1.1什么是线程安全性 要对线程安全性给出一个确切的定义是非常复杂的.最核心的概念就是正确性.正确性:某个类的行为与其规范完全一致.在良好的规范中通常会定义各种不变性条件来约束对象的状态,以及定义各种 ...

  9. Java并发:五种线程安全类型、线程安全的实现、枚举类型

    1. Java中的线程安全 Java线程安全:狭义地认为是多线程之间共享数据的访问. Java语言中各种操作共享的数据有5种类型:不可变.绝对线程安全.相对线程安全.线程兼容.线程独立 ① 不可变 不 ...

  10. Java并发编程实战 之 线程安全性

    1.什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何种调用方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全 ...

随机推荐

  1. 【日常踩坑】从 SSLEOFError 到正确配置 Proxy

    目录 踩坑 代理服务器 普通的代理服务器 因国家法律规定,部分内容已删除,完整内容请查看文章末尾链接 代理配置 追根溯源 urllib3 pip 万恶之源 urllib 参考资料 本文主要参考 Pyt ...

  2. 《SQL与数据库基础》20. 主从复制

    目录 主从复制 原理 搭建 主库配置 从库配置 测试 本文以 MySQL 为例 主从复制 主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执 ...

  3. 【故障公告】一而再,再而三,三翻四复:数据库服务器 CPU 100%

    会员救园,故障捣乱,每当困难时,故障们总是喜欢雪上加霜过来考验你. 今天下班前 17:43~17:47 期间,园子的 SQL Server 数据库服务器突然出现 CPU 100% 问题. 发现问题后, ...

  4. 蚂蚁集团混沌工程 ChaosMeta V0.5 版本发布

    混沌工程 ChaosMeta 的全新版本 V0.5 现已正式发布!该版本包含了许多新特性和增强功能,为用户提供了支撑混沌工程各个阶段的平台能力,以及降低使用门槛的用户界面. ChaosMeta V0. ...

  5. IP协议:连接你我,掌握互联网的关键

    IP 基本认识 在之前的章节中,我们已经详细介绍了应用层和传输层的相关概念和原理,了解了进程之间如何进行可靠的数据传输.我们知道,传输层的头部包含了进程所使用的端口信息,这是为了确保数据能够正确地传递 ...

  6. open3d -- voxel_down_sample

    官网文档 parameter: Input: open3d.geometry.Pointcloud点云类 voxel_size: 体素单位长度 Return: 处理后的点云类 Description: ...

  7. 教育法学第八章单元测试MOOC

    第八章单元测试 返回 本次得分为:100.00/100.00, 本次测试的提交时间为:2020-09-06, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 单选(5分) 社团法人与财团法 ...

  8. 铅华洗尽,粉黛不施,人工智能AI基于ProPainter技术去除图片以及视频水印(Python3.10)

    视频以及图片修复技术是一项具有挑战性的AI视觉任务,它涉及在视频或者图片序列中填补缺失或损坏的区域,同时保持空间和时间的连贯性.该技术在视频补全.对象移除.视频恢复等领域有广泛应用.近年来,两种突出的 ...

  9. 【Unity3D】Cesium加载大地图

    1 前言 ​ Cesium 是一个地球可视化平台和工具链,具有数据切片.数据分发.三维可视等功能. ​ Cesium 支持 JS.Unity.Unreal.O3DE.Omniverse 等平台,框架如 ...

  10. Zuul 2.1.5 设计分析

    前言 https://github.com/Netflix/zuul zuul 是 SpringCloud 家族老兵,使用 Java 微服务大部分都在使用 zuul 作为网关.既然他如此重要,那么我们 ...