线程安全之synchronized&volatile

作者: 小石读史 | 来源:发表于2020-06-19 22:38 被阅读0次

1.参考资料
《Java并发编程艺术》
·网络资料
。https://blog.csdn.net/zqz_zqz/article/details/70246212
。https://www.cnblogs.com/ZoHy/p/11313155.html
。https://www.cnblogs.com/linghu-java/p/8944784.html
。https://blog.csdn.net/qq_32099833/article/details/103721326
。https://www.cnblogs.com/chenyangyao/p/5269622.htm
2.线程安全介绍
当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替运行,也不需要进行额外的同步,或者在调用方进行任何其他的
协调操作,调用这个对象的行为都可以获取正确的结果,那这个对象是线程安全的。
3.线程安全问题
(1)先看两段代码
代码片段1

private volatile int count=0;
public void unsafe Add(throws InterruptedException(
int threadCount=50000;
Executor Service executor=Executors.new Fixed ThreadPool(10) ;
long start=System.current Time Mill isO
for(inti=0; i<threadCount; i++) (
Runnable runnable=O->count++;
executor.execute(runnable) ;
)
executor.shutdown() ;
executor.await Termination(Long.MAX_VALUE, Time Unit.DAYS) ;
System.out.println(”unsafe Add耗时:”+(System.current Time Millis() -start) +”ms”) ;
System.out.printLn(”unsafe Add执行结果:count=”+count) ;
)

执行结果?
代码片段2

private Atomic Integer atomic Integer=new Atomic Integer(0) ;
public void safe Add() throws InterruptedException(
int threadCount=50000;
Executor Service executor=Executors.new Fixed ThreadPool(10) ;
Long start=System.current Time Mill isO;
for(inti=0; i<threadCount; i++) (
Runnable runnable=(->atomic Integer.increment And Get O;
executor.execute(runnable) ;
~
executor.shutdown O;
executor.await Termination(Long.MAX_VALUE, Time Unit.DAYS) ;
System.out.printLn(”safe Add耗时:”+(System.current Time Millis() -start) +”ms”) ;
System.out.printLn(”safe Add执行结果:count=”+atomic Integer.get O) ;
)

执行结果?
4.为什么会出现线程安全问题????
一、volatile
1.介绍
在多线程并发编程中synchronized和volatile都扮演着重要的角色, volatile是轻量级的synchronized, 它在多处理器开发中保证了共享变量的“可见
性”。可见性的意思是当一个线程修改一个共享变量时, 另外一个线程能读到这个修改的值。如果volatile变量修饰符使用恰当的话, 它比
synchronized的使用和执行成本更低, 因为它不会引起线程上下文的切换和调度, volatile还可以禁止指令重排序
2.线程可见性
(1)代码

public class Volatile Main(
private boolean flag=true;
public void run O) (
while(flag) (
System.out.printLn(”end”) ;
)
public static void main(String[] args) throws Exception(
VolatiLe Main volatile Main=new VolatiLe Main O;
new Thread(O->volatile Main.run() .star tO;
Thread.sleep(1000) ;
volatile Main.flag=false;
System.out.printLn(”flag=true”) ;
)

(2)内存模型


image.png

3.指令重排序
先看代码

public class Possible Reordering(
static in tx=0, y=0;
static int a=0, b=0;
public static void main(String args) throws InterruptedException(
Thread one=new Thread(new Runnable O) (
public void run() (
a=1;
x=b;
));
Thread other=new Thread(new Runnable O(
public void run O(
b=1;
y=a;
);
one.star tO; other.star tO;
one.join O; other.join O;
System.out.printLn(“(”+x+“, ”+y+“) ”) ;

大多数现代微处理器都会采用将指令乱序执行(out-of-order execution, 简称OoO E或OOE) 的方法, 在条件允许的情况下, 直接运行当前有能力
立即执行的后续指令,避开获取下一条指令所需数据时造成的等待3。通过乱序执行的技术,处理器可以大大提高执行效率。
除了处理器, 常见的Java运行时环境的JIT编译器也会做指令重排序操作, 即生成的机器指令与字节码指令顺序不一致。
4.as-if-serial语义
As-if-serial语义的意思是, 所有的动作(Action) 都可以为了优化而被重排序, 但是必须保证它们重排序后的结果和程序代码本身的应有结果是一致的。Java编译器、运行时和处理器都会保证单线程下的as-if-serial语义。
再看一段代码
int a=1;
in tb=2;
intc=a+b;
查看字节码指令
1.I CONST_1将int类型常量1压入栈
I STORE 1将int类型值存入局部变量
2.I CONST_2将int类型常量2压入栈
I STORE 2将int类型值存入局部变量
3.I LOAD 1从局部变量中装载int类型值
4.I LOAD 2从局部变量中装载int类型值

  1. I ADD执行int类型的加法
    6.I STORE 3将int类型值存入局部变量
    问题:以下哪几种重排不可能存在
    (1、2、4)
    (2、1、3)
    (1、3、5)
    (3、2、4)
    (4、1、3)
    (2、4、5)
    二CAS
    1.介绍
    Compare and Swap, 即比较再交换
    2.Atomic Integer
    (1)代码分析
Atomic Integer atomic Integer=new Atomic Integer(1) ;
System.out.printLn(atomic Integer.increment And Get O) ;
public final int increment And Get() (
return unsafe.get And Add Int(this, value Offset, 1) +1;
public final int get And Add Int(Object var 1, long var 2, int var 4) (
int var 5;
do(
var 5=this.get Int VolatiLe(var 1, var 2) ;
)while(!this.comp are And Swap Int(var 1, var 2, var 5, var 5+var 4) ) ;
return var 5;
)

(2)操作流程
1.读取当前值E
2.计算结果值V
3.比较E和当前新值N
3-1.相等更新为新值V
3-2.不相等重新执行刚才操作
(3) ABA问题
T1想将A从100修改为101,此时T1读取到100
此时T2将A从100改为101,又改为100
T 1进行CAS操作时发现A还是100, 符合条件, 所以修改100为101。但是此时A已经不是原来的A了。
xx交了一个女朋友,一个月后女朋友要去出差3个月,3个月后女朋友回来了,告诉xx说怀孕2个月了
(4) 如何解决ABA
引入版本号Atomic Stamped Reference
三、synchronized
1.介绍
同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方
法完成的。
一句话总结出Synchronized的作用:能够保证在同一时刻最多只有一个线程执行该段代码, 以达到保证并发安全的效果
2.使用场景
(1)修饰一个代码块

/**
*修饰代码块
*/
public void block Sync() (
System.out.println(”普通方法开始执行前”+Thread.current Thread) .getName() ) ;
synchronized(this) (
System.out.println(”代码块开始执行”+Thread.current Thread() .getName O) ;
//代码逻辑
try(
Thread.sleep(5000) ;
) catch(InterruptedException e) (
e.printStackTrace O;
)
System.out.printLn(”普通方法执行结束”+Thread.current Thread() .getName() ) ;
)

被修饰的代码块称为同步语句块,其作用的范围是大括号括起来的代码,作用的对象是调用这个代码块的对象。
(2)修饰一个方法

/**
*修饰方法
*/
public synchronized void method Sync O(
//代码逻辑
System.out.println(”普通方法执行:”+Thread.current Thread(.getName() ) ;
try(
Thread.sleep(5000) ;
) catch(InterruptedException e) (
e.printStackTrace(;
)
System.out.printLn(”普通方法执行结束”+Thread.current Thread() .getName() ) ;
)

被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
(3)修饰一个静态的方法

/**
*修饰静态方法
*/
public static synchronized void static Method Sync() (
//代码逻辑
System.out.printLn(”静态方法开始执行:”+Thread.current Thread() .getName O) ) ;
try(
Thread.sleep(5000) ;
) catch(InterruptedException e) (
e.printStackTrace O;
)
System.out.printLn(”静态方法执行结束”+Thread.current Thread O.getName() ) ;

其作用的范围是整个静态方法,作用的对象是这个类的所有对象。
(4)修饰一个类

/**
*修饰类
*/
public void clazz Sync() (
System.out.printLn(”修饰类开始执行前”+Thread.current Thread() .getName() ) ;
synchronized(Sync.class) (
System.out.printLn(”修饰类开始执行”+Thread.current Thread() .getName() ) ;
//代码逻辑
try(
Thread.sleep(5000) ;
) catch(InterruptedException e) (
e.printStackTrace O;
)
System.out.printLn(”修饰类执行结束”+Thread.current Thread O.getName() ) ;
)

其作用的范围是synchronized后面括号括起来的部分, 作用的对象是这个类的所有对象。

相关文章

  • 线程安全之synchronized&volatile

    1.参考资料《Java并发编程艺术》·网络资料。https://blog.csdn.net/zqz_zqz/art...

  • 多线程之线程安全性

    多线程环境下使用非线程安全类会导致线程安全问题。线程安全问题表现为原子性,有序性,可见性 在讲述线程安全三大特性之...

  • Android 多线程探索(四)— 同步集合

    前言 Android JDK 提供了一系列线程安全的集合,避免多线程环境下由于线程安全导致的各种问题。 一、程序之...

  • HashMap学习

    概述 线程非安全,并且允许key与value都为null值,HashTable与之相反,为线程安全,key与val...

  • Vector

    前言 这个和ArrayList百分之90感觉都是类似的只是这个是线程安全的 ArrayList 不是线程安全的 代...

  • java并发之ThreadLocal

    java并发之ThreadLocal 知识导读 ThreadLocal主要作用于线程的上下文,而不是线程安全,如果...

  • Rx 从 SinkDisposer 一窥线程安全

    首先我们要分析下需求,哪些操作是需要保证线程安全的。 显然_state的设置是要保证线程安全的, 那么与之相关的读...

  • atomic & nonatomic

    什么是线程安全??? 线程安全:多线程操作共享数据不会出现想不到的结果就是线程安全的,否则,是线程不安全的。 at...

  • ConcurrentHashMap源码设计分析

    二、线程安全(Thread-safe)的集合对象:● Vector 线程安全● HashTable 线程安全● S...

  • HashMap 和 Hashtable 的区别

    线程安全: HashMap 是非线程安全的,而 Hashtable 是线程安全的,因为 Hashtable 内部的...

网友评论

    本文标题:线程安全之synchronized&volatile

    本文链接:https://www.haomeiwen.com/subject/vksaghtx.html