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. [ABC143E] Travel by Car

    2023-02-20 题目 题目传送门 翻译 翻译 难度&重要性(1~10):4.5 题目来源 AtCoder 题目算法 最短路 解题思路 我们枚举每一对点 \((u_i,v_i)\) 间的距 ...

  2. Antd Form表单中Input输入框 在IE9下按下任何按键都会报校验失败

    antd Form表单中Input输入框 在IE9下按下任何按键都会报校验失败,导致输入框输入不了任何内容! 可能你的react及react-dom版本由于过高导致antd组件不能兼容,需要对reac ...

  3. mpi转以太网连接300PLC与易控INSPEC上位机软件通讯

    300PLC转以太网与易控 INSPEC 上位机软件通讯 易控 INSPEC 软件通过以太网连接西门子 S7300 系列 PLC, 兴达易控MPI-ETH-XD1.0通讯模块为 PLC 提供以太网通讯 ...

  4. 新零售SaaS架构:面向中小连锁的SaaS系统整体规划

    零售企业的发展路径 零售企业的发展路径一般可分为以下几个阶段: 单店经营阶段:企业在一个地区或城市开设单个门店.这时,企业需要把精力放在了解当地市场和顾客需求上,这是积累经验和品牌知名度的重要环节.为 ...

  5. CAS中ABA问题的解决

    转自(here)   CAS问题的产生 在运用CAS做Lock-Free操作中有一个经典的ABA问题: 线程1准备用CAS将变量的值由A替换为B,在此之前,线程2将变量的值由A替换为C,又由C替换为A ...

  6. 2023-10-04:用go语言,现有一棵无向、无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge

    2023-10-04:用go语言,现有一棵无向.无根的树,树中有 n 个节点,按从 0 到 n - 1 编号 给你一个整数 n 和一个长度为 n - 1 的二维整数数组 edges , 其中 edge ...

  7. MySQL系列之——MySQL介绍和安装、MySQL简介及产品线、安装方式(源码安装 rpm方式 yum方式 通用二进制安装)

    文章目录 一 MySQL介绍和安装 1.1 什么是数据? 1.2 什么是数据库管理系统(DBMS)? 1.3 数据库管理系统种类 二 MySQL简介及产品线 2.1 MySQL行业主流版本 2.2 企 ...

  8. idea2020.3 安装插件JetBrains 插件市场安装 Cloud Toolkit

    <Cloud Toolkit User Guide> 本文是 Alibaba Cloud Toolkit 的使用文档指引,所有相关的使用参考,都可以在本文中找到.如果在使用中有任何问题,请 ...

  9. OKR 是什么?

    OKR OKR 是什么? OKR(Objectives and Key Results)目标与关键结果管理法,起源于英特尔,后在谷歌发扬光大. OKR 是一套协助组织进行目标管理的工具和方法,旨在促进 ...

  10. C#学习笔记---异常捕获和变量

    异常捕获 使用异常捕获可以捕获出现异常的代码块,防止因为异常抛出造成的程序卡死的情况发生. try{}catch{}finally{}结构 //异常捕获 try { string str=Consol ...