首页 > 编程语言 >Java并发(十八)----常见线程安全类及实例分析

Java并发(十八)----常见线程安全类及实例分析

时间:2023-12-12 21:22:47浏览次数:37  
标签:Java void update ---- 安全 线程 new public

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,void,update,----,安全,线程,new,public
From: https://www.cnblogs.com/xiaoyh/p/17157344.html

相关文章

  • 【双栈实现队列】Java——Stack类
    leetcode232.用栈实现队列题意:双栈实现队列;要求每个入队、出队操作均摊O(1)复杂度题解:用一个栈in维护入队元素,另一个栈out维护出队元素出队或取队头元素:首先判断栈out是否为空,如果为空,将栈in中的元素pop()到栈out中,那么栈out栈顶元素即为原队列队头元素。(米奇妙妙屋啊~)判断......
  • 2023-12-12
    好久都没写过算法题了,上次提交也是一个月之前的事了。 其实第一讲和第二讲都没怎么学好,因此现在开始学第三讲又嗝了这么长时间感觉图论是真的抽象,一个树的重心就把我困了三四天,现在也才终于有了自己的见解了,真的是给我整吐了,距离其实已经过了三个月了,本来是打算三个月把......
  • Python高级之闭包函数
    闭包函数【一】闭包函数的定义闭包(Closure)是指在函数内部定义的函数,并且这个内部函数可以访问外部函数的变量。这种机制允许函数保留对它创建时可见的变量的访问权,即使在其生命周期结束后也可以使用。闭包的主要特点是:内部函数定义在外部函数内部。内部函数可以引用外部函数......
  • Python项目之员工管理系统-函数版
    员工管理系统#完成以下功能'''---------------员工系统---------------1:注册2:登陆3:添加员工信息4:查看指定员工信息5:查看所有员工信息6:删除指定员工信息......
  • Excel-排序和筛选
    一、排序单条目排序多条目排序【根据订单好、日期进行排序,注意先订单号,再日期】 自定义排序二、筛选 ......
  • 【leetcode 239. 滑动窗口最大值】Java优先队列——PriorityQueue类
    leetcode239.滑动窗口最大值题目描述:1e5大小的nums[]数组中长度为k(1<=k<=1e5)的窗口的最大值题解:暴力求解O(n^2)会超时,需要O(nlogn)的解法使用大根堆优先队列维护窗口元素,每次取最大值复杂度降为O(1),堆结构维护复杂度O(logn)问:如果维护窗口[l,r]前[0,l-1]的元素不影......
  • vscode settings.json
    {"workbench.startupEditor":"none","workbench.iconTheme":"vscode-icons","workbench.colorTheme":"Dracula","window.title":"${rootName}${separator}${profileName}",......
  • Go语言学习笔记
    Go语言入门教程:https://c.biancheng.net/golang/Go语言的基本类型有:boolstringint、int8、int16、int32、int64uint、uint8、uint16、uint32、uint64、uintptrbyte//uint8的别名rune//int32的别名代表一个Unicode码float32、float64complex64、complex128当......
  • 事后诸葛亮分析报告
    1.作业概述这个作业属于哪个课程软件工程这个作业的要求在哪里事后分析这个作业的目标事后诸葛亮分析报告一、事后分析设想和目标我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述?我们达到目标了么(原计划的功能做到了几......
  • Java 8 Stream 流的常用方法总结
    Java8Stream流的常用方法总结Java8引入了一个新的API:StreamAPI,它允许我们以声明式的方式处理数据集合。StreamAPI提供了一系列强大的方法,可以帮助我们更简洁、高效地处理数据。本文将总结Java8Stream流的常用方法,并提供相应的代码示例。1.创建Stream首先,我们需要了......