Java并发之基础知识

同一进程的所有线程

  • 彼此独立运行
  • 共享进程的内存地址空间
  • 访问相同的变量并在同一堆上分配对象

线程安全性

单语句不一定有原子性,可能包含多个操作
例value++
1. 读取value
2. value+1
3. 写入value

线程安全守则

  • 不在线程之间共享状态变量
  • 将状态变量修改为不可变的变量
  • 在访问状态变量时进行同步

servlet线程安全性

它既不包含任何域,也不包含任何对其他类中域的引用
临时状态仅存在于线程栈上的局部变量中,只能由当前线程访问

无状态对象一定是线程安全的

竞态条件

非线程安全域+非线程安全操作 => 多线程的交替执行时序 => 影响正确性

先检查后执行 => “延迟初始化” => A,B都通过了检查,然后AB都进行初始化 => 冲突


加锁机制

内置锁

静态的synchronize方法以class对象作为锁

重入

“重入”意味着获取锁的操作的粒度是线程,而不是“调用”。
子类可以进入父类的synchronize块


对象共享

可见性

线程变量 <——-> 主线程变量
并非即时发生

重排序

没有同步时,编译器,处理器以及运行时,都可能对操作的执行顺序进行调整

volatile变量:轻量级弱同步

将变量的更新通知到其他线程,不会重排序,不会被缓存

线程封闭:只在单线程内访问数据

servlet 单线程同步方式

Servlet类本质上也是一个普通的类,并且Servlet容器默认只允许单个实例存在。当请求达到服务器的时候,Servlet实例如果已经存在的话则直接加载该实例,如果该Servlet类还未实例化则会先初始化这个Servlet。当请求到达Web服务器时,Web服务器中有一个线程池,它会从线程池中取一个工作线程,通过该线程调用请求的Servlet。因此,对Servlet来说,可以同时被好几个请求调用。请求结束后,线程放回线程池。

这种设计带来的好处是,Servlet单实例,减少了生成Servlet的开销。通过线程池响应请求,避免了不断创建线程和销毁线程的开销,提高了性能。但是这种多线程操纵单实例的模式,也会有一些副作用,那就是可能造成数据的不一致。

ThreadLocal类:当前线程保存的信息
当线程终止后,这些值会作为垃圾回收

扫描二维码关注公众号,回复: 4503300 查看本文章
ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

不变性

  • 不可变对象一定线程安全
  • final域能确定初始化过程的安全性,在共享时无须同步
Final 变量在并发当中,原理是通过禁止cpu的指令集重排序(重排序详解http://ifeve.com/java-memory-model-1/ http://ifeve.com/java-memory-model-2/),来提供现成的课件性,来保证对象的安全发布,防止对象引用被其他线程在对象被完全构造完成前拿到并使用。

与前面介绍的锁和volatile相比较,对final域的读和写更像是普通的变量访问。对于final域,编译器和处理器要遵守两个重排序规则:

在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

与Volatile 有相似作用,不过Final主要用于不可变变量(基本数据类型和非基本数据类型),进行安全的发布(初始化)。而Volatile可以用于安全的发布不可变变量,也可以提供可变变量的可见性。

猜你喜欢

转载自blog.csdn.net/wsh596823919/article/details/82733231
今日推荐