时间:2021-05-20
Java多线程 线程同步与死锁
1.线程同步
多线程引发的安全问题
一个非常经典的案例,银行取钱的问题。假如你有一张银行卡,里面有5000块钱,然后你去银行取款2000块钱。正在你取钱的时候,取款机正要从你的5000余额中减去2000的时候,你的老婆正巧也在用银行卡对应的存折取钱,由于取款机还没有把你的2000块钱扣除,银行查到存折里的余额还剩5000块钱,准备减去2000。这时,有趣的事情发生了,你和你的老婆从同一个账户共取走了4000元,但是账户最后还剩下3000元。
使用代码模拟下取款过程:
public class ThreadTest { public static void main(String[] args) { // 创建一个账户,里面有存款5000元 Account account = new Account(5000); // 模拟取钱过程 GetMoney getMoney = new GetMoney(account); new Thread(getMoney, "你").start(); new Thread(getMoney, "你老婆").start(); }}class GetMoney implements Runnable { private Account account; public GetMoney(Account account) { super(); this.account = account; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "账户现在有" + account.getMoney() + "元"); // 使效果更明显,休眠10ms try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } int money = account.getMoney() - 2000; account.setMoney(money); System.out.println(Thread.currentThread().getName() + "取了2000元,账户现在有" + account.getMoney() + "元"); }}class Account { private int money; public Account(int money) { super(); this.money = money; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; }}看下打印信息:
你账户现在有5000元你老婆账户现在有5000元你取了2000元,账户现在有3000元你老婆取了2000元,账户现在有3000元同步锁
从上面的案例可以看出,当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。
Java中可以使用synchronized关键字来取得一个对象的同步锁。
修改一下上面的案例,在run方法中加入同步锁:
@Overridepublic void run() { synchronized (this) { System.out.println(Thread.currentThread().getName() + "账户现在有" + account.getMoney() + "元"); // 使效果更明显,休眠10ms try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } int money = account.getMoney() - 2000; account.setMoney(money); System.out.println(Thread.currentThread().getName() + "取了2000元,账户现在有" + account.getMoney() + "元"); }}看下打印信息:
你账户现在有5000元你取了2000元,账户现在有3000元你老婆账户现在有3000元你老婆取了2000元,账户现在有1000元当你取钱的时候,取款机锁定了你的账户,不允许其他人对账户进行操作,当你取完钱后,取款机释放了你的账户,你的老婆才可以取钱。
2.死锁
同步锁虽好,但也要科学使用,不然就会发生死锁,何为死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。
举个栗子,两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。
下面用一段简单的代码来模拟死锁:
public class DeadlockTest { public static void main(String[] args) { String str1 = new String("资源1"); String str2 = new String("资源2"); new Thread(new Lock(str1, str2), "线程1").start(); new Thread(new Lock(str2, str1), "线程2").start(); }}class Lock implements Runnable { private String str1; private String str2; public Lock(String str1, String str2) { super(); this.str1 = str1; this.str2 = str2; } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "运行"); synchronized (str1) { System.out.println(Thread.currentThread().getName() + "锁住" + str1); Thread.sleep(1000); synchronized (str2) { // 执行不到这里 System.out.println(Thread.currentThread().getName() + "锁住" + str2); } } } catch (Exception e) { e.printStackTrace(); } }}看下打印信息:
线程1运行线程2运行线程1锁住资源1线程2锁住资源2第一个线程锁住了资源1(甲占有桥的一部分资源),第二个线程锁住了资源2(乙占有桥的一部分资源),线程1企图锁住资源2(甲让乙退出桥面,乙不从),进入阻塞,线程2企图锁住资源1(乙让甲退出桥面,甲不从),进入阻塞,死锁了。
死锁的产生是有规律可循的,只有同时满足以下四个条件,死锁才会产生。
1.互斥条件:一个资源每次只能被一个进程使用。独木桥每次只能通过一个人。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。乙不退出桥面,甲也不退出桥面。
3.不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺。甲不能强制乙退出桥面,乙也不能强制甲退出桥面。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。如果乙不退出桥面,甲不能通过,甲不退出桥面,乙不能通过。
知道了死锁产生的必要条件,在开发中就很容易避免死锁问题了。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
java多线程死锁相信有过多线程编程经验的朋友,都吃过死锁的苦。除非你不使用多线程,否则死锁的可能性会一直存在。为什么会出现死锁呢?我想原因主要有下面几个方面:
Java中CountDownLatch进行多线程同步详解CountDownLatch介绍在前面的Java学习笔记中,总结了Java中进行多线程同步的几个方法:1
Java线程优先级需要避免的与多任务处理有关的特殊错误类型是死锁(deadlock)。死锁发生在当两个线程对一对同步对象有循环依赖关系时。例如,假定一个线程进入
java多线程的几种实现方法总结1.多线程有几种实现方法?同步有几种实现方法?多线程有两种实现方法,分别是继承Thread类与实现Runnabl
0.关于线程同步(1)为什么需要同步多线程?线程的同步是指让多个运行的线程在一起良好地协作,达到让多线程按要求合理地占用释放资源。我们采用Java中的同步代码块