美文网首页javaAndroid知识Android技术知识
Java多线程 线程同步与死锁

Java多线程 线程同步与死锁

作者: 容华谢后 | 来源:发表于2017-02-25 15:58 被阅读433次
封面

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关键字来取得一个对象的同步锁。

// Object可以为任何对象,表示当前线程取得该对象的锁。
synchronized (Object) {
}

修改一下上面的案例,在run方法中加入同步锁:

@Override
public 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.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。如果乙不退出桥面,甲不能通过,甲不退出桥面,乙不能通过。

知道了死锁产生的必要条件,在开发中就很容易避免死锁问题了。

3.写在最后

欢迎同学们吐槽评论,如果你觉得本篇博客对你有用,那么就留个言或者点下喜欢吧(^-^)

相关文章

  • Synchronized关键字详解2(对非object持锁)

    4.死锁:同步方法容易造成死锁。Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待根本不可能被释放的锁...

  • Java多线程 线程同步与死锁

    1.线程同步 多线程引发的安全问题 一个非常经典的案例,银行取钱的问题。假如你有一张银行卡,里面有5000块钱,然...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • Java自学-多线程 死锁

    Java 演示多线程死锁 当业务比较复杂,多线程应用里有可能会发生死锁 步骤 1 : 演示死锁 线程1 首先占有对...

  • Android中的多线程

    1. Java多线程基础 Java多线程,线程同步,线程通讯 2. Android常用线程 HandlerThre...

  • 无标题文章

    多线程死锁的出现是因为多个线程进行同步的时候才会出现,所以首先要说一下多线程同步的问题,说到同步就会提到synch...

  • iOS多线程(一)

    多线程涉及到的概念: 进程,线程,主线程,任务,队列,死锁,串行,并行,同步,异步,GCD,NSOperation...

  • 多线程--同步与锁

    同步与锁 上一篇中,笔者介绍了Java多线程的基础知识,主要讲解了进程/线程的区别、Java多线程的创建、Java...

  • 18 多线程死锁

    多线程死锁产生的原因:在同步中嵌套同步,会出现死锁的现象。 查找到当前JVM环境变量C:\Program File...

  • 照本宣科的问题

    3条你遵循的多线程最佳实践三条最佳实践我觉得大多数Java程序员都应该遵循: 如何避免死锁?Java多线程中的死锁...

网友评论

  • Aldrich_N:应该不会死锁,使用1的时候2没被另一个线程使用
    容华谢后: @Aldrich_N 两个线程是同时执行的,线程1锁住了资源1,线程2锁住了资源2,线程1企图锁住资源2,但是资源2已经被线程2锁住了,线程2企图锁住资源1,但是资源1已经被线程1锁住了,然后就死锁了。
  • 9711922c6b29:感觉例子不会死锁啊
    容华谢后: @水手辛巴 写两个线程应该会清楚些
    9711922c6b29: @水手辛巴 哦,构造方法里把顺序调换了

本文标题:Java多线程 线程同步与死锁

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