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. [ABC146F] Sugoroku

    2023-02-27 题目 题目传送门 翻译 翻译 难度&重要性(1~10):5 题目来源 AtCoder 题目算法 贪心 解题思路 对于第 ii 个点,只要到达 \(s_{i+1}\cdot ...

  2. mysql到底需不需要容器化?

    前言:在容器化的时代,当然一切皆可容器化.在docker官网首页赫然有下面这几个大字.足以知道docker的优势.那么且问,mysql适合跑在docker中吗? 当然,这个问题有人说可以,也有人说不可 ...

  3. Python+Flask接口实现简单的ToKen功能

    话不多说,上代码 from flask import request, jsonify from functools import wraps class TokenRequired: @classm ...

  4. JSTL fn函数使用

    首先,我们要在页面的最上方引用: <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/function ...

  5. Verilog实现定点乘法器

    实验目的 理解定点乘法的不同实现算法的原理,掌握基本实现算法. 熟悉并运用 Verilog 语言进行电路设计. 为后续设计 CPU 的实验打下基础. 实验内容 定点乘法器有多种实现,实验要求实现迭代乘 ...

  6. crm--纯后端部署

    博客地址:https://www.cnblogs.com/zylyehuo/ 技术栈:supervisor + nginx + uwsgi + django + virtualenv + mariad ...

  7. 2.10 PE结构:重建重定位表结构

    Relocation(重定位)是一种将程序中的一些地址修正为运行时可用的实际地址的机制.在程序编译过程中,由于程序中使用了各种全局变量和函数,这些变量和函数的地址还没有确定,因此它们的地址只能暂时使用 ...

  8. Oracle-降低表的高水位线

    在应用中存在一系列的表,对表的操作是批量插入又批量删除,最终导致表的水位线很高.高水位线影响全索引扫描的SQL.即影响系统的性能. 现有方法降低表的水位线: 1.降低表的高水位线 select 'al ...

  9. DP 复习

    背包 约定使用 \(v_i\) 表示放入第 \(i\) 件物品的花费,\(w_i\) 表示第 \(i\) 件物品的价值,背包容量 \(M\),物品件数 \(N\). 01 背包 每种物品仅有一件,可以 ...

  10. nodejs修改npm包安装位置

    适用于非个人电脑.便携使用 npm config set cache D:\nodejs\node_cache npm config set prefix D:\nodejs npm config s ...