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. CF992E Nastya and King-Shamans 题解

    传送门 分析 由于满足 \(a_i\ge0\),所以 \(s_i\) 单调不减. 当我们找到一个 \(i\) 时,不管 \(i\) 是否满足,下一个可能的一定大于等于 \(a_i+s_{i-1}\). ...

  2. RocketMQ系列(一) 基本介绍

    RocketMQ系列(一) 基本介绍 1.MQ 作用 MQ 的应用场景主要包含以下 3 个方面: 1.1.异步与解耦 当我们下了一个订单之后,订单服务会进行 RPC 同步调用 支付服务.库存服务.物流 ...

  3. 非全自研可视化表达引擎-RuleLinK

    说在前面 工作中经常会遇到这样的场景: 帮忙把小贝拉门店 商品金额在5w以内,产康订单最多95折. 帮忙把圣贝拉门店 开业时间在6个月内,折扣低于7折要发起审批 帮忙把宁波太平洋店设置独立合同模板 帮 ...

  4. KRPano插件一键解密大师 支持最新版KRPano XML/JS解密 ,支持分析下载静态/动态网站资源

    KRPano插件一键解密大师,可以一键解密KRPano的XML/JS插件,并可以分析下载静态和动态网站的所有资源.软件下载安装即可使用,解密仅需鼠标一键点击即可,无需配置任何开发环境,方便全景开发人员 ...

  5. Java SE 21 新增特性

    Java SE 21 新增特性 作者:Grey 原文地址: 博客园:Java SE 21 新增特性 CSDN:Java SE 21 新增特性 源码 源仓库: Github:java_new_featu ...

  6. Java新特性中的Preview功能如何运行和调试

    在每个Java新版本发布的特性中,都会包含一些Preview(预览)功能,这些功能主要用来给开发者体验并收集建议.所以,Preview阶段的功能并不是默认开启的. 如果想体验某个Java版本中的Pre ...

  7. 文心一言 VS 讯飞星火 VS chatgpt (106)-- 算法导论10.1 4题

    四.用go语言,重写 ENQUEUE 和 DEQUEUE 的代码,使之能处理队列的下溢和上溢. 文心一言: 在 Go 语言中,我们可以使用切片来实现队列.下面是如何实现 ENQUEUE 和 DEQUE ...

  8. Django-rest-framework框架——过滤排序分页异常处理、自动生成接口文档、JWT认证

    @ 目录 一 过滤Filtering 二 排序 三 分页Pagination 可选分页器 应用 四 异常处理 Exceptions 4.1 使用方式 4.2 案例 4.3 REST framework ...

  9. IDEA工具第一篇:细节使用-习惯设置

    安装好Idea后,直接上手clone代码进入编码时代,有没有那么一刻你会觉用起来没有那么顺手流畅呢? 下面是关于 [Windows] 下安装idea的一些习惯设置[ Mac大致一样 ] 一.修改系统文 ...

  10. docker入门加实战—从部署MySQL入门docker

    docker入门加实战-从部署MySQL入门docker docker部署MySQL 输入如下命令: docker run -d \ --name mysql \ -p 3306:3306 \ -e ...