zoukankan      html  css  js  c++  java
  • 多线程----线程同步

    当多个线程对同一个资源进行操作时,有可能引发线程安全问题:如下图所示:

    输出结果:

    出现这种情况的原因是因为当一个线程还未执行扣款操作时,由于CPU的切换,另一个线程获得CPU资源,在上一个线程做出扣款操作之前也进入了循环。

    java提供的多种保障线程安全的解决方案,常见的有一下方式:

    (1)使用synchronized实现同步方法
    每一个用synchronized关键字声明的方法都是临界区。在Java中,同一个对象的临界区,在同一时间只有一个允许被访问。

    运行结果:

    补充:
    1、synchronized关键字会降低应用程序的性能,因此只能在并发场景中修改共享数据的方法上使用它。
    2、临界区的访问应该尽可能的短。方法的其余部分保持在synchronized代码块之外,以获取更好的性能
    (2)使用重入锁实现线程同步
    在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
    ReenreantLock类的常用方法有:
    ReentrantLock() : 创建一个ReentrantLock实例
    lock() : 获得锁
    unlock() : 释放锁

    补充:ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用。
    公平锁和非公平锁
    公平锁是指多个线程等待同一个锁时,必须按照申请锁的先后顺序来一次获得锁
    特点:等待锁的线程不会饿死,但整体效率相对低一些
    非公平锁是指可以不按照顺序,可以抢占锁
    特点:整体效率高,但有些线程会饿死或者说很早就在等待锁,但要等很久才会获得锁
    重入锁有这样一个构造函数,对公平性进行设置。当fair为true时,表示此锁是公平的。

    例如:

    测试如下:

    死锁

    当一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞,这个我们都知道。如果线程1持有锁A并且想获得锁B,线程2持有锁B并且想获得锁A,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。
    当一组Java线程发生死锁时,这两个线程就永远不能再使用了,并且由于两个线程分别持有了两个锁,那么这两段同步代码/代码块也无法再运行了----除非终止并重启应用。
    死锁是设计的BUG,问题比较隐晦。不过死锁造成的影响很少会立即显现出来,一个类可能发生死锁,并不意味着每次都会发生死锁,这只是表示有可能。当死锁出现时,往往是在最糟糕的情况----高负载的情况下。

    测试方法:

    输出结果:

    等了n久也不见输出结果。
    补充:如何定位死锁问题:
    (1)输入jps命令,获取当前Java虚拟机进程的pid

     (2)jstack+pid命令,打印堆栈:

    这时候可以利用taskkill命令去终止没有被Terminate的进程:

    如何避免死锁:
    1、让程序每次至多只能获得一个锁。当然,在多线程环境下,这种情况通常并不现实
    2、设计时考虑清楚锁的顺序,尽量减少嵌在的加锁交互数量
    3、既然死锁的产生是两个线程无限等待对方持有的锁,那么只要等待时间有个上限不就好了。当然synchronized不具备这个功能,但是我们可以使用Lock类中的tryLock方法去尝试获取锁,这个方法可以指定一个超时时限,在等待超过该时限之后变回返回一个失败信息

  • 相关阅读:
    通过设置iis在局域网中访问网页
    .NetCore集成Dapr踩坑经历
    Api跨域设置
    Win10安装.net2.0/3.0
    C# MVC 全局错误Application_Error中处理(包括Ajax请求)
    使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)
    C#设计模式学习笔记:(4)建造者模式
    .NET CORE(C#) WPF简单菜单MVVM绑定
    值类型与引用类型的区别
    [博客美化]给你博客添加一个萌萌的看板娘吧
  • 原文地址:https://www.cnblogs.com/red-evil/p/10039071.html
Copyright © 2011-2022 走看看