美文网首页
【多线程不安全的原因与解决办法】

【多线程不安全的原因与解决办法】

作者: hello高world | 来源:发表于2017-02-08 20:25 被阅读0次

    1、 线程不安全的特征

    通过上面一个博文,我们可以注意到不安全例子的特点:

    • 有实例变量或静态变量
    • 类似于count++(读-操作-写)
    • 有if-then
    • 有发布set方法
    • 多个线程对同一个对象操作

    如果有碰到上面这样的,请要注意了!!!

    2、不安全的原因

    2.1 原子性

    • count++ 不是原子操作
    • ConcurrentHashMapTest中的if-then也不是原子操作

    2.1 有序性

    有序性问题(指令重排或者工作内存和主存同步延迟)
    见上一篇博客中的——神奇的while例子, 底层可能优化成这样:

    while(!done)
       i++;
    

    转成这样了:

    if(!done) {
       while(true)
       i++;
    }
    

    指令被重排了,工作线程done无法同步到主存中。

    2.3 可见性

    比如上面的例子,主线程更改了done,但子线程看不到done的变化。

    3 保存并发三个特性的手段

    • 原子性:AtomicXX、synchronized、使用Map的原子操作。等等
    • 有序性: synchronized、volatile
    • 可见性: synchronized、volatile、final
      !!!万能的synchronized。

    4 为什么?

    • 为什么AtomicXX有原子性功效?
    • 为什么synchronized有原子性、可见性、有序性功效?
    • 为什么volatile有可见性、有序性功效?

    请看规定的:
    1、Java内存模型中的八条可保证happen—before的规则
    2、Java内存模型中的八条交互规则
    3、volatile特殊规则

    套用上面规则来分析synchronized,其他类似。

    • 可见性:【规定的某条规则】对一个变量执行unlock之前,必须先把此变量同步回主内存中。
    • 有序性和原子性:【规定的某条规则】一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁

    5、 synchronized是什么鬼?

    javap后的synchronized方法.png 具体方法

    synchronized修饰的代码库底层指令集有两个: monitorenter/monitorexit

    <b>1、 monitor是什么鬼?</b>
    <b>2、 为什么会有两个monitorexit</b>
    -> 第二个monitorexit是怕你出了异常,做了释放锁的动作。

    6、 什么是monitor?

    7、为什么要引入管程?

    解决类似以下两个问题 from《现代操作系统》:

    • 火车订票系统,只有最后一张票,但有2个客户试图争夺这张火车票。
    • 线程A负责读取mysql数据,线程B负责打印数据。线程B需要等待A的完成,否则打印就是不正确的。

    即: <b>两个或多个线程读写某些共享数据,而最后取决于线程运行的精确时序。——竞争条件</b>

    如何避免竞争条件?
    ——互斥。线程A对共享资源使用完之前线程B无法使用。

    互斥的手段:
    信号量、互斥量、管程、CPU中断等

    8、信号量解决互斥

    8.1 什么是信号量

    自行补脑: http://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html

    8.2 解决互斥栗子(来自《现代操作系统》)

    生产者和消费者问题.png

    注意:信号量大于=0,如果=0,再去获取信号量的话,就会阻塞住。

    问题:交换下生产者中的两行,如下:

    Paste_Image.png
    • <b>请分析当empty=0和full=10情况,是不是会发生死锁?嘿嘿。。
      所以管程的优势是,有编译器来处理进入临界区代码和出临界区代码。我们只要加了synchronized,由编译器自动帮我们编织了monitorenter和monitorexit。
      </b>
    管程织入

    还有一个问题就是进入临界区(共享数据的读写代码段称为临界区)之后,如果里面因为一个条件需要阻塞,那这个锁就无法释放对吧。
    所以管程模型,提出了类似于java中的wait/notify/notifyAll(条件队列)用于解决这个问题。当条件阻塞,调用wait让出这个锁。当条件满足notify,让另一个线程重新获得这个锁。

    <b>所以wait/notify/notifyAll(条件队列)一定要在synchronized中使用,否则会报错</b>

    相关文章

      网友评论

          本文标题:【多线程不安全的原因与解决办法】

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