Android开发Java 杂谈

Java并发:多线程安全总结

2019-05-31  本文已影响7人  smartzheng

1.并发基础

定义:一个cpu“同时”处理多个任务,而多个线程都在争取这个cpu资源

1.1 优点

1.2 缺点

1.3 相关概念

2.线程基础

2.1 创建线程的方式

2.2 线程的状态

java并发_线程的状态.png

2.3 线程状态基本操作

2.3.1 stop

2.3.2 interrupt / isInterrupted / interrupted

2.3.3 join

2.3.4 sleep

2.3.5 wait

2.3.6 yield

2.4 Daemon守护线程

3.Java内存模型(JMM)

当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替运行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获取正确的结果,那这个对象是线程安全的。——《深入理解Java虚拟机》

多线程条件下,会涉及多个线程间相互通信;为了性能优化,还会涉及编译器指令重排序和处理器指令重排序等问题。

而出现线程安全的主要原因就是主内存和工作区内存数据不一致,理解这些问题的核心在于JMM

3.1 内存模型抽象结构

并发编程中主要需要解决两大问题,即线程之间的通信和同步问题。通信主要有两种机制:共享内存和消息传递。Java选择的是共享内存的方式。

3.1.1共享变量类型

在java程序中所有实例域,静态域和数组元素都是放在堆内存中(所有线程均可访问到,是可以共享的),而局部变量,方法定义参数和异常处理器参数不会在线程间共享。共享数据会出现线程安全的问题,而非共享数据不会出现线程安全的问题。

3.1.2抽象结构模型

3.2 重排序

为了提高性能,编译器和处理器常常会对指令进行重排序;编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序

3.3 happens-before

JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证:如果A线程的写操作a与B线程的读操作b之间存在happens-before关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见

基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。主要规则如下:

3.4 并发数据安全

从JMM内存抽象结构hehappens-before规则看来,单线程情况下,不会出现数据安全问题,但多线程情况下,可能出现数据脏读问题,多线程开发时需要从原子性,有序性,可见性三个方面进行考虑

4Synchronized与对象锁机制

4.1 Synchronized

//实例方法,锁住的是该类的实例对象
public synchronized void method(){}
//静态方法,锁住的是该类的类对象
public static synchronized void method(){}
//锁住的是该类的实例对象
synchronized(this){}
//锁住的是该类的类对象
synchronized(Object.class){}
//锁住的是配置的实例对象
synchronized(new Obeject()){}

注:如果锁的是类对象的话,尽管new多个实例对象,但他们仍然是属于同一个类依然会被锁住,即线程之间保证同步关系。

4.2 对象锁机制

4.2.1 Java对象头

4.2.2 synchronized实现

5锁

锁按照不同的方式可以分为多种类型,以下总结了常见的分类方式:

5.1 独享锁 VS 共享锁

5.2 悲观锁 VS 乐观锁

5.3 自旋锁 VS 适应性自旋锁

5.4 CAS

5.4.1 原理

5.4.2 存在问题及解决方案

5.5 无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁

如果同步代码块中的内容过于简单,状态转换消耗的时间有可能比用户代码执行的时间还要长”。这种方式就是synchronized最初实现同步的方式,也JDK 6之前synchronized效率低的原因。这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”,JDK 6中为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”。
Java对象头里的Mark Word记录了锁的4种状态,级别从低到高依次是:无锁、偏向锁、轻量级锁和重量级锁。锁状态只能升级不能降级。

6volatile关键字

6.1 简介

6.2 实现原理(缓存一致性)

6.3 与happens-before关系

7final关键字

final可以修饰变量,方法和类,用于表示所修饰的内容一旦赋值之后就不会再被改变,而这个值得属性可以改变

7.1 final变量

当final变量未初始化时系统不会进行隐式初始化

7.1.1 final成员变量:

7.1.1 final局部变量:

7.2 final方法

7.3 final类

7.4 final与重排序

7.4.1 final基本数据类型

7.4.2 final引用数据类型

注:对final域写重排序规则可以确保我们在使用一个对象引用的时候该对象的final域已经在构造函数被初始化过了,这一点还有一个前提是:在构造函数中,不能让这个被构造的对象被其他线程可见(虽然一个对象的final域构造函数return前被初始化,但是如果该对象的引用被其他线程使用,会导致在构造函数执行的过程中其他线程通过该对象获取到还未初始化的final域)

8多线程安全性总结

处理多线程数据安全,指的就是处理多线程情况下数据的原子性、有序性和可见性问题

8.1 原子性

指一个操作是不可中断的,要么全部执行成功要么全部执行失败

8.2 有序性

8.3 可见性

8.4 总结

参考:
你听___的博客
不可不说的Java“锁”事

上一篇下一篇

猜你喜欢

热点阅读