线程安全性与synchronized

线程安全:多线程访问某个类时,这个类始终都能表现出正确的行为,这个类就是线程安全的。

简单的说,就是多线程执行的结果与单线程执行的结果始终一致,不会因为多线程的执行时序不同而出现不同的结果

以下是一个线程不安全的程序:

当这段代码在单线程中执行时,会得出正确的答案,而在多线程环境中,则出现了执行结果完全靠运气,结果依赖于线程之间的执行时序,显然违背了线程安全的定义:多线程访问某个类时,这个类始终都能表现出正确的行为。

为什么会出现这样的结果?

虽然count++看上去很像是一个操作,实际在执行的时候是三个独立操作:

  1. 读取count的值。

  2. 将值+1。

  3. 写入count。

当上面这一个“读取-修改-写入”的操作没有使用同步机制保证这三步操作是一个不可分割原子操作,就会出现不同线程的这三步操作交替执行。

只需要加上一些线程协调机制便可将这三步操作作为一个原子操作,比如同步锁:

以上现象有一个术语来形容,叫竞态条件,即多线程访问同一资源时,如果对资源访问顺序敏感,执行结果依赖于线程的执行顺序,则为竞态条件。

Synchronized是java内置的锁,这是一种互斥锁,只能由一个线程进入被锁保护的代码,因此这个锁保护的代码块以原子方式执行,可以提供对象锁/类锁/安全发布全局变量的功能,

Synchronized的几种用法:

1.****对象锁:

2.****类锁:

使用锁时需要注意的地方:

  1. 当需要锁来协调线程对某个变量的访问时,所有访问这个变量的位置需要用同一个锁。

2.使用锁时要清楚代码块的代码是否需要执行很长时间,比如网络和IO操作,如果长时间持有锁,会造成线程竞争,等待的线程有两种等待策略:

a.忙锁:自旋等待锁释放,适合代码块执行时间很短的情况。

b.闲锁:将等待的线程挂起,锁释放后在合适的时机上下文切换,有一定内存同步代价。

jvm会对synchronized代码块进行不同程度的锁膨胀,偏向锁、轻量级锁、重量级锁,这个在后面的jvm系列博文会总结到。

当代码块执行的任务需要很长时间时尽量不要加锁。

同步代码块应尽可能短小,不需要同步的代码尽量移出,代码越短小,执行时间越短,线程竞争就越少,性能和吞吐量会更好,简单说就是快进快出

一些优化synchronized互斥锁性能的方法:

  1. 分段锁。

  2. 读写锁。

线程封闭

1. ad-hoc封闭:维护线程封闭性的职责完全由程序来承担。

例如代码中不做任何同步处理,只是把线程不安全的程序做成单线程程序,只有一个线程来执行了,自然就不会存在什么竞态条件、资源竞争,不推荐使用,建议用ThreadLocal。

在volatile变量上存在一种特殊的线程封闭。只要你能确保只有单个线程对共享的volatile变量执行写入操作,那么就可以安全地在这些共享的volatile变量上执行"读取—修改—写入"的操作。这种情况下相当于将修改操作封闭在单个线程中以防止发生竞态条件,并且volatile的可见性还确保其他的线程能看到最新的值。

  1. 栈封闭

简单说就是不用全局变量,用局部变量,方法内部声明的局部变量作用域是封闭在单个线程里的,不对其它线程共享,自然就不会出现竞态条件。



为什么栈封闭的局部变量能保证线程安全呢?后面更新的JVM系列会说明运行过程中的栈帧结构。

3. ThreadLocal

是一种更规范的维护线程封闭性的方式,使变量和持有变量的线程关联起来,每个变量都有一份自己的变量,相互隔离防止共享。

例如实现线程与用户信息的绑定:

不可变对象,不能修改,只读共享

不可变的成员变量不需要额外同步,天生线程安全,如果该成员变量是一个对象,则所有属性都需要声明为final保证不可变性。

如图static保证了jvm安全发布该变量,发布后对该类创建的所有线程都是可见的,final保证了发布后为只读,不可写。

当多线程程序存在线程安全问题时,选择解决方法的优先级应当如下:

1.能否做成无状态的不变对象。无状态是最安全的。

2.能否线程封闭。

3.采用何种同步技术。

线程安全与synchronized的更多相关文章

  1. Java线程同步(synchronized)——卖票问题

    卖票问题通常被用来举例说明线程同步问题,在Java中,采用关键字synchronized关键字来解决线程同步的问题. Java任意类型的对象都有一个标志位,该标志位具有0,1两种状态,其开始状态为1, ...

  2. 线程池 队列 synchronized

    线程池 BlockingQueue synchronized volatile 本章从线程池到阻塞队列BlockingQueue.从BlockingQueue到synchronized 和 volat ...

  3. Java线程锁,synchronized、wait、notify详解

    (原) JAVA多线程这一块有点绕,特别是对于锁,对锁机制理解不清的话,程序出现了问题也很难找到原因,在此记录一下线程的执行以及各种锁. 1.JAVA中,每个对象有且只有一把锁(lock),也叫监视器 ...

  4. 从线程池到synchronized关键字详解

    线程池 BlockingQueue synchronized volatile 前段时间看了一篇关于"一名3年工作经验的程序员应该具备的技能"文章,倍受打击.很多熟悉而又陌生的知识 ...

  5. java线程学习之synchronized关键字

    关键字synchronized的作用是实现线程间的同步.它的任务是对同步的代码加锁.一个代码块同时只能有同一个线程进行读和写操作,从而保证线程间是安全的. 线程安全的概念是:当多个线程访问某一个类(对 ...

  6. [多线程] 线程中的synchronized关键字锁

    为什么要用锁? 在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实 ...

  7. Java基础-多线程-③线程同步之synchronized

    使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...

  8. java高并发系列 - 第10天:线程安全和synchronized关键字

    这是并发系列第10篇文章. 什么是线程安全? 当多个线程去访问同一个类(对象或方法)的时候,该类都能表现出正常的行为(与自己预想的结果一致),那我们就可以所这个类是线程安全的. 看一段代码: pack ...

  9. 线程安全之 synchronized 和 ReentrantLock

    线程安全之 synchronized 和 ReentrantLock + 面试题 前面我们介绍了很多关于多线程的内容,在多线程中有一个很重要的课题需要我们攻克,那就是线程安全问题.线程安全问题指的是在 ...

随机推荐

  1. 按图索骥,一些mysql知识点

    有事没事多看看 基础知识考察 基础知识,尤其是一些理论知识,例如: MySQL有哪些索引类型,这是个半开放式命题: 从数据结构角度可分为B+树索引.哈希索引.以及不常用的FULLTEXT索引(现在My ...

  2. 索引 'GXHRCS.PK_A253' 或这类索引的分区处于不可用状态

    ORA-01502: 索引 'GXHRCS.PK_A253' 或这类索引的分区处于不可用状态 http://blog.sina.com.cn/s/blog_7ab8d2720101ozw6.html ...

  3. 猜想-未做 利用office组件读取excel数据

    ---未实际使用过 用SQL-Server访问Office的Access和Excel http://blog.sina.com.cn/s/blog_964237ea0101532x.html 2007 ...

  4. Scaner对象

    目录 Scaner的基本概念 基本语法: 1.使用next() 的方式来接收字符串(使用频率较少) 2.使用nextLine()的方式来接收字符串 进阶使用(练习题) Scaner的基本概念 之前我们 ...

  5. 在 n 道题目中挑选一些使得所有人对题目的掌握情况不超过一半。

    Snark and Philip are preparing the problemset for the upcoming pre-qualification round for semi-quar ...

  6. java——assert(断言)方法

    包:org.junit.Assert; assertEqual(a,b,[msg='测试失败时打印的信息']): 断言a和b是否相等,相等则测试用例通过. assertNotEqual(a,b,[ms ...

  7. 同一父进程下的子进程之间的通信(pipe通信)

    首先对于fork命令  通过fork命令创建进程 父进程返回子进程id 子进程返回0 失败返回-1 对于pipe通讯机制   pipe通讯是半双工的 也就是说只能一方读一方写 题目中想要P1的输出作为 ...

  8. HTML特殊符号整理

    往网页中输入特殊字符,需在html代码中加入以&开头的字母组合或以&#开头的数字.下面就是以字母或数字表示的特殊符号大全.                               ...

  9. 转 vue动画总结

    使用过渡类名(有进入及出去,适合显示隐藏,需要配合v-if) .v-enter,//进入前 .v-leave-to {//离开后 只需要入场动画 可以把v-leave-to删掉 opacity: 0; ...

  10. git简单的使用步骤

    Git介绍 Git是分布式版本控制系统 集中式VS分布式,SVN VS Git 1)SVN和Git主要的区别在于历史版本维护的位置 2)这两个工具主要的区别在于历史版本维护的位置Git本地仓库包含代码 ...