文章目录

  • 简介
  • 不同的加锁顺序
  • 使用private类变量
  • 使用相同的Order
  • 释放掉已占有的锁

简介

java中为了保证共享数据的安全性,我们引入了锁的机制。有了锁就有可能产生死锁。

死锁的原因就是多个线程锁住了对方所需要的资源,然后现有的资源又没有释放,从而导致循环等待的情况。

通常来说如果不同的线程对加锁和释放锁的顺序不一致的话,就很有可能产生死锁。

不同的加锁顺序

我们来看一个不同加锁顺序的例子:

public class DiffLockOrder {private int amount;public DiffLockOrder(int amount){this.amount=amount;}public void transfer(DiffLockOrder target,int transferAmount){synchronized (this){synchronized (target){if(amount< transferAmount){System.out.println("余额不足!");}else{amount=amount-transferAmount;target.amount=target.amount+transferAmount;}}}}
}

上面的例子中,我们模拟一个转账的过程,amount用来表示用户余额。transfer用来将当前账号的一部分金额转移到目标对象中。

为了保证在transfer的过程中,两个账户不被别人修改,我们使用了两个synchronized关键字,分别把transfer对象和目标对象进行锁定。

看起来好像没问题,但是我们没有考虑在调用的过程中,transfer的顺序是可以发送变化的:

        DiffLockOrder account1 = new DiffLockOrder(1000);DiffLockOrder account2 = new DiffLockOrder(500);Runnable target1= ()->account1.transfer(account2,200);Runnable target2= ()->account2.transfer(account1,100);new Thread(target1).start();new Thread(target2).start();

上面的例子中,我们定义了两个account,然后两个账户互相转账,最后很有可能导致互相锁定,最后产生死锁。

使用private类变量

使用两个sync会有顺序的问题,那么有没有办法只是用一个sync就可以在所有的实例中同步呢?

有的,我们可以使用private的类变量,因为类变量是在所有实例中共享的,这样一次sync就够了:

public class LockWithPrivateStatic {private int amount;private static final Object lock = new Object();public LockWithPrivateStatic(int amount){this.amount=amount;}public void transfer(LockWithPrivateStatic target, int transferAmount){synchronized (lock) {if (amount < transferAmount) {System.out.println("余额不足!");} else {amount = amount - transferAmount;target.amount = target.amount + transferAmount;}}}
}

使用相同的Order

我们产生死锁的原因是无法控制上锁的顺序,如果我们能够控制上锁的顺序,是不是就不会产生死锁了呢?

带着这个思路,我们给对象再加上一个id字段:

    private final long id; // 唯一ID,用来排序private static final AtomicLong nextID = new AtomicLong(0); // 用来生成IDpublic DiffLockWithOrder(int amount){this.amount=amount;this.id = nextID.getAndIncrement();}

在初始化对象的时候,我们使用static的AtomicLong类来为每个对象生成唯一的ID。

在做transfer的时候,我们先比较两个对象的ID大小,然后根据ID进行排序,最后安装顺序进行加锁。这样就能够保证顺序,从而避免死锁。

    public void transfer(DiffLockWithOrder target, int transferAmount){DiffLockWithOrder fist, second;if (compareTo(target) < 0) {fist = this;second = target;} else {fist = target;second = this;}synchronized (fist){synchronized (second){if(amount< transferAmount){System.out.println("余额不足!");}else{amount=amount-transferAmount;target.amount=target.amount+transferAmount;}}}}

释放掉已占有的锁

死锁是互相请求对方占用的锁,但是对方的锁一直没有释放,我们考虑一下,如果获取不到锁的时候,自动释放已占用的锁是不是也可以解决死锁的问题呢?

因为ReentrantLock有一个tryLock()方法,我们可以使用这个方法来判断是否能够获取到锁,获取不到就释放已占有的锁。

我们使用ReentrantLock来完成这个例子:

public class DiffLockWithReentrantLock {private int amount;private final Lock lock = new ReentrantLock();public DiffLockWithReentrantLock(int amount){this.amount=amount;}private void transfer(DiffLockWithReentrantLock target, int transferAmount)throws InterruptedException {while (true) {if (this.lock.tryLock()) {try {if (target.lock.tryLock()) {try {if(amount< transferAmount){System.out.println("余额不足!");}else{amount=amount-transferAmount;target.amount=target.amount+transferAmount;}break;} finally {target.lock.unlock();}}} finally {this.lock.unlock();}}//随机sleep一定的时间,保证可以释放掉锁Thread.sleep(1000+new Random(1000L).nextInt(1000));}}}

我们把两个tryLock方法在while循环中,如果不能获取到锁就循环遍历。

本文的代码:

learn-java-base-9-to-20/tree/master/security

本文已收录于 http://www.flydean.com/java-security-code-line-dead-lock/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

java安全编码指南之:死锁dead lock相关推荐

  1. Java - 死锁 Dead Lock 定位分析

    文章目录 Pre jstack Thread dump Dead Lock 分析 分析代码 解决 Pre JVM-11虚拟机性能监控与故障处理工具之[JDK的可视化工具-JConsole] jstac ...

  2. java安全编码指南之:lock和同步的正确使用

    文章目录 简介 使用private final object来作为lock对象 不要synchronize可被重用的对象 不要sync Object.getClass() 不要sync高级并发对象 不 ...

  3. c++11 多线程编程(四)------ 死锁(Dead Lock)

    死锁 如果你将某个mutex上锁了,却一直不释放,另一个线程访问该锁保护的资源的时候,就会发生死锁,这种情况下使用lock_guard可以保证析构的时候能够释放锁,然而,当一个操作需要使用两个互斥元的 ...

  4. java安全编码指南之:文件IO操作

    文章目录 简介 创建文件的时候指定合适的权限 注意检查文件操作的返回值 删除使用过后的临时文件 释放不再被使用的资源 注意Buffer的安全性 注意 Process 的标准输入输出 InputStre ...

  5. java安全编码指南之:线程安全规则

    文章目录 简介 注意线程安全方法的重写 构造函数中this的溢出 不要在类初始化的时候使用后台线程 简介 如果我们在多线程中引入了共享变量,那么我们就需要考虑一下多线程下线程安全的问题了.那么我们在编 ...

  6. java安全编码指南之:ThreadPool的使用

    文章目录 简介 java自带的线程池 提交给线程池的线程要是可以被中断的 正确处理线程池中线程的异常 线程池中使用ThreadLocal一定要注意清理 简介 在java中,除了单个使用Thread之外 ...

  7. java安全编码指南之:可见性和原子性

    文章目录 简介 不可变对象的可见性 保证共享变量的复合操作的原子性 保证多个Atomic原子类操作的原子性 保证方法调用链的原子性 读写64bits的值 简介 java类中会定义很多变量,有类变量也有 ...

  8. java安全编码指南之:输入校验

    文章目录 简介 在字符串标准化之后进行校验 注意不可信字符串的格式化 小心使用Runtime.exec() 正则表达式的匹配 简介 为了保证java程序的安全,任何外部用户的输入我们都认为是可能有恶意 ...

  9. java安全编码指南之:字符串和编码

    文章目录 简介 使用变长编码的不完全字符来创建字符串 char不能表示所有的Unicode 注意Locale的使用 文件读写中的编码格式 不要将非字符数据编码为字符串 简介 字符串是我们日常编码过程中 ...

最新文章

  1. numpy使用[]语法索引二维numpy数组中倒数N列数据列的数值内容(accessing the last N columns in numpy array)
  2. 常考数据结构与算法:反转链表
  3. 【中国好公司】中国人最向往的公司:BAT相差太多!华为排名出乎意料
  4. c++类的嵌套(1)
  5. torch repeate 的用法
  6. 74ls20设计半加器_组合逻辑电路(半加器全加器及逻辑运算)实验报告
  7. 我在飞机上,飞机在哪里?浅谈飞行模式与GPS定位
  8. “未安装任何音频输出设备”解决办法
  9. Ubuntu18.04安装QQ For Linux
  10. bps和pps各自是什么意思?
  11. 看短视频上瘾的背后-推荐算法
  12. 如何关闭打开文件安全警告
  13. 专访 | Apache Pulsar PMC 成员翟佳:社区的信任最重要
  14. adobe air for ubuntu + markman 安装?
  15. 自动化办公1-文件夹文件分类器
  16. [英语语法]词法之动词:时态语态
  17. matlab模拟风场竖桥向时程,大跨度桥梁三维脉动风场的计算机模拟
  18. [http-nio-8080-exec-9] com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getPoolManager
  19. 公路广告牌问题(Highway Billboard Problem)--动态规划
  20. 微软VS2010广告全集,让程序员们内牛满面

热门文章

  1. 0007-Reverse Integer(整数反转)
  2. 【数据结构】哈夫曼树与哈夫曼编码
  3. codeforces 数论分析题
  4. deque,list,queue,priority_queue
  5. delphi中的第三方控件如何安装
  6. 【网络编程】之一、初识WinSocket
  7. 裂墙推荐!IntelliJ IDEA 常用插件一览,让效率成为习惯
  8. Jedis对redis的操作详解
  9. JAVA通信编程(五)——串口通讯的补充说明
  10. 几十万实例线上系统的抖动问题定位