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. KVM下windows由IDE模式改为virtio模式蓝屏 开不开机

    KVM安装Windows默认使用的是qemu虚拟化IDE硬盘模式,在这种情况下,IO性能比较低,如果使用virtio的方式可以提高虚拟机IO性能. 于是我想将这台虚拟机迁移到openstack中管理 ...

  2. 大规模 IoT 边缘容器集群管理的几种架构-6-个人体验及推荐

    前文回顾 大规模 IoT 边缘容器集群管理的几种架构-0-边缘容器及架构简介 大规模 IoT 边缘容器集群管理的几种架构-1-Rancher+K3s 大规模 IoT 边缘容器集群管理的几种架构-2-H ...

  3. 【matplotlib基础】--坐标轴

    Matplotlib的坐标轴是用于在绘图中表示数据的位置的工具. 坐标轴是图像中的水平和垂直线,它们通常表示为 x 轴和 y 轴.坐标轴的作用是帮助观察者了解图像中数据的位置和大小,通常标有数字或标签 ...

  4. BeanUtils.copyProperties:曾经是我的女神,现在是我的毒药。

    前言 BeanUtils.copyProperties十有八九是你这些年工作中用的很多的其中一个,不管是Apache的还是Spring的. 网上的解释浩如烟海,我这边用一个超简单的例子直观展示给你看. ...

  5. 响应式编程——初识 Flux 和 Mono

    by emanjusaka from ​ https://www.emanjusaka.top/archives/4 彼岸花开可奈何 本文欢迎分享与聚合,全文转载请留下原文地址. 前言 Reactor ...

  6. HTML一键打包APK工具 如何进行实名认证购买和激活

    HTML一键打包APK工具 价格表 授权时长 价格 1小时 49 1天 99 1个月 199 1个季度 399 半年 599 1年 799 付费版功能 功能点 免费版 付费版 去除广告信息 × √ 去 ...

  7. 解锁Java面试中的锁:深入了解不同类型的锁和它们的用途

    简介 多线程编程在现代软件开发中扮演着至关重要的角色.它使我们能够有效地利用多核处理器和提高应用程序的性能.然而,多线程编程也伴随着一系列挑战,其中最重要的之一就是处理共享资源的线程安全性.在这个领域 ...

  8. Django-rest-framework框架——Web应用模式、API接口、接口测试工具(Postman)、RESTfulAPI规范、序列化、drf、环境安装与配置、CBV源码分析、 APIView

    @ 目录 一 Web应用模式 1.1 前后端不分离 1.2 前后端分离 二 API接口 三 接口测试工具:Postman 四 RESTful API规范(背诵牢记) 4.1 数据的安全保障 4.2 接 ...

  9. TIM-有感BLDC转速解析

    TIM-有感BLDC转速解析 1.基本概念解析 霍尔传感器的原理:通电线圈产生的磁场会使得转子所在位置会产生磁场,其中离得最近的霍尔传感器的磁场最强,进而导致最近霍尔传感器会产生最大的电压信号,这个最 ...

  10. Cloud Bursting解决方案,Serverless容器降本增效极致体验

    本文分享自华为云社区<DTSE Tech Talk | 第42期:Cloud Bursting解决方案,Serverless容器降本增效极致体验>,作者:华为云社区精选. 线下IDC自建K ...