线程安全性和共享

概念:一般指多个并发线程对同一个类进行操作时候,这个类始终都能表现出正确的行为

原子性:原子性一般指一系列的操作要么全部成功,要么全部失败

  • 竞态条件:由于不正确的执行顺序所造成的错误结果的情况,通常都是并发场景下对同一共享资源的读写冲突
  • 先检查后执行:由于检查和执行是两个原子性,甚至更多个原子性操作,所以会导致竞态条件的产生
  • 复合操作:即使都是原子性操作,但由于多个原子性操作的组合操作,同样会导致竞态条件的产生

加锁机制:

  • 内置锁:Java为每个对象都支持了内部锁,synchronized默认用到了this对象的锁
  • 重入:锁的重入一般指的是A线程获取到锁之后,再次执行到同一个加锁代码,还是可以获取到此锁。不支持这种方式的话,会造成继承扩展的一些死循环。锁可以重入,也证明了锁绑定的是线程而不是调用

锁保护状态

  锁会以一种串行的方式来执行代码,以保证状态的一致性

活跃性与性能

  过多的同步机制会严重影响性能,但多线程不执行同步机制,极有可能导致安全性问题

基于上面的原因:

我们一般致力于减少同步代码的代码块,甚至不采用同步机制,一般认为有几种方案

1、栈封闭:常见的应用就是Servlet,servlet是没有状态的,只通过一个方法来调用,也就是说所有的变量都由方法内部产生,也就是说作用域就在方法内,不存在线程共享数据问题

2、线程封闭:ThreadLocal,线程变量。就是把一个变量和一个线程绑定在一起,可以看起来一些apache工具类中对日期格式化的代码中就用到了这个线程变量,因为SimpleDataFormat是线程不安全的类;相当于只有一个线程可以修改数据。 一般也可以通过Volatile控制变量的可见性,再通过控制仅单一线程可以修改数据来保证原子性

3、只读对象:不可变对象和事实不可变对象,理论上只要不涉及数据的写;那么仅仅是读,就是安全的。事实不可变对象,就像是使用lombda表达式的时候,如果内部用到了外部变量,外部变量从被赋值其并未被修改,那么可以直接引用,要是被修改过值,那就必须对引用的变量进行final变量的重新引用

4、线程安全容器:比如ConcurrentHashMap等一些线程安全的容器可以直接用于多线程调用,线程的安全性在容器内部实现,而无需在外部显示调用锁机制

5、保护对象:即用指定的锁来保护对象,仅持有锁的线程可以访问对象

原文地址:https://www.cnblogs.com/gabin/p/13492029.html