博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
多个线程之间共享数据的方式(卖票问题,存取款问题)
阅读量:7226 次
发布时间:2019-06-29

本文共 7021 字,大约阅读时间需要 23 分钟。

多线程共享数据,其实要分为两种情况:

1.多线程执行相同的代码处理数据,最经典的问题就是卖票;

2.多线程执行不同的代码处理数据,最经典的问题就是银行存取钱。

卖票问题探究:

最初的代码是:

public class Test1 {    public static void main(String[] args) {        Ticket target = new Ticket();        Thread threadA = new Thread(target, "A");        Thread threadB = new Thread(target, "B");        threadA.start();        threadB.start();    }}class Ticket implements Runnable {    private int leftTicket = 500;    @Override    public void run() {        while (leftTicket > 0) {            leftTicket = leftTicket - 1;            System.out.println(Thread.currentThread().getName() + "处理后还剩"                    + leftTicket + "张票");        }    }}

      以上代码创建了两个线程,因为创建这两个线程时传的是同一个Runnable对象,所以如果这个Runnable对象有成员变量的话,这两个线程就可都操作这个Runnable对象的成员变量。

      执行上面代码多次,发现基本上每次打印的结果都不相同,这就是线程不安全。所谓线程安全就是多线程每次执行的结果都是固定的、可控的。为解决线程安全问题,就要用到同步监视器,synchronized或者 Lock。

如果用传统的synchronized的话,有两种方式:

1.synchronized同步代码块:synchronized(this){...}或者synchronized(Object obj){...}。同步代码块放在run方法体中,相对于下面的synchronized同步方法,推荐优先使用synchronized同步代码块

2.synchronized同步方法:用synchronized修饰普通方法,可以有返回值,也可以没有返回值,在run方法中调用此同步方法。

synchronized同步代码块解决卖票问题:

 

public class Test1 {    public static void main(String[] args) {        Ticket target = new Ticket();        Thread threadA = new Thread(target, "A");        Thread threadB = new Thread(target, "B");        threadA.start();        threadB.start();    }}class Ticket implements Runnable {    private int leftTicket = 500;    @Override    public void run() {        while (leftTicket > 0) {            synchronized (this) {                leftTicket = leftTicket - 1;                System.out.println(Thread.currentThread().getName() + "处理后还剩"                        + leftTicket + "张票");            }        }    }}

 

这样,每次执行的结果都一样,且leftTicket 的值是依次减小的。

银行存取款问题探究:

     存取款问题,因为存款与取款是对同一账户的成员变量进行操作,但是执行方法体不一样,所以需要创建两种线程。首先创建一个账户类,此类有一个余额成员变量。实例化这两种线程时要传同一个账户类对象,这样这两种线程操作的就是同一个账户对象也就是同一个账户的余额成员变量了。

     如果在这两种线程的run方法中用synchronized同步代码块的话,则需要有2种synchronized同步代码块。因为同步代码块中必然会操作到余额变量,但是没有在线程类中声明此余额变量,只能通过账户的get方法得到余额,进而进行操作:

/** * 账户类,包含余额成员变量 *  */class Account {    private double balance;    private boolean flag = false;    public double getBalance() {        return balance;    }    public void setBalance(double balance) {        this.balance = balance;    }    public boolean isFlag() {        return flag;    }    public void setFlag(boolean flag) {        this.flag = flag;    }    public Account(double balance) {        super();        this.balance = balance;    }}class DrawThread extends Thread {    private Account account;    private double drawAmount;    public DrawThread(String name, Account account, double drawAmount) {        super(name);        this.account = account;        this.drawAmount = drawAmount;    }    @Override    public void run() {        while (true) {            synchronized (account) {                try {                    if (!account.isFlag()) {                        account.wait();                    } else {                        account.setBalance(account.getBalance() - drawAmount);                        System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:"                                + account.getBalance());                        account.setFlag(false);                        account.notifyAll();                    }                } catch (Exception e) {                    e.printStackTrace();                }            }        }    }}// 存款class DepositThread extends Thread {    private Account account;    private double depositAmount;    public DepositThread(String name, Account account, double depositAmount) {        super(name);        this.account = account;        this.depositAmount = depositAmount;    }    @Override    public void run() {        while (true) {            synchronized (account) {                try {                    if (account.isFlag()) {                        account.wait();                    } else {                        account.setBalance(account.getBalance() + depositAmount);                        System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:"                                + account.getBalance());                        account.setFlag(true);                        account.notifyAll();                    }                } catch (Exception e) {                    e.printStackTrace();                }            }        }    }}public class ThreadTest {    public static void main(String[] args) {        Account acct = new Account(0);        for (int i = 1; i <= 2; i++) {            new DrawThread("取款者" + i, acct, 500).start();            new DepositThread("存款者" + i, acct, 500).start();        }    }}

       如果使用synchronized同步方法的话,需要有两种synchronized同步方法。在run方法中调用synchronized同步方法,为了避免上面的共享变量的声明的麻烦,可以把synchronized同步方法声明在账户类中,这样在线程类中就不用声明该共享变量了:

/** * 账户类,包含余额成员变量 *  */class Account {    private double balance;    private boolean flag = false;    public Account(double balance) {        super();        this.balance = balance;    }    // 取款    public synchronized void draw(double drawAmount) {        try {            if (!flag) {                this.wait();            } else {                balance -= drawAmount;                System.out.println(Thread.currentThread().getName() + "取现" + drawAmount + ",账户余额为:" + balance);                flag = false;                this.notifyAll();            }        } catch (Exception e) {            e.printStackTrace();        }    }    // 存款    public synchronized void deposit(double depositAmount) {        try {            if (flag) {                this.wait();            } else {                balance += depositAmount;                System.out.println(Thread.currentThread().getName() + "存款" + depositAmount + ",账户余额为:" + balance);                flag = true;                this.notifyAll();            }        } catch (Exception e) {            e.printStackTrace();        }    }}class DrawThread extends Thread {    private Account account;    private double drawAmount;    public DrawThread(String name, Account account, double drawAmount) {        super(name);        this.account = account;        this.drawAmount = drawAmount;    }    @Override    public void run() {        while (true) {            account.draw(drawAmount);        }    }}// 存款class DepositThread extends Thread {    private Account account;    private double depositAmount;    public DepositThread(String name, Account account, double depositAmount) {        super(name);        this.account = account;        this.depositAmount = depositAmount;    }    @Override    public void run() {        while (true) {            account.deposit(depositAmount);        }    }}public class ThreadTest {    public static void main(String[] args) {        Account acct = new Account(0);        for (int i = 1; i <= 2; i++) {            new DrawThread("取款者" + i, acct, 500).start();            new DepositThread("存款者" + i, acct, 500).start();        }    }}

 

转载于:https://www.cnblogs.com/koushr/p/5873462.html

你可能感兴趣的文章
《Unreal Engine 4蓝图可视化编程》一2.2 制作瞄准镜效果
查看>>
《树莓派用户指南(第3版)》——1.5 关于Model B的PCB版本修订历史
查看>>
《WebGL入门指南》——第1章,第1.3节WebGL原生API
查看>>
《树莓派Python编程入门与实战(第2版)》——3.5 关于Python交互式shell
查看>>
《Android安全技术揭秘与防范》—第2章2.2节安全的发展趋势
查看>>
《AngularJS高级程序设计》——5.6 使用JavaScript运算符
查看>>
Storm入门之附录B
查看>>
vnStatSVG: 流量监控软件 vnStat 最佳 Web 前端
查看>>
《Python数据分析》一2.2 创建多维数组
查看>>
《C++面向对象高效编程(第2版)》——1.5 什么可以作为类
查看>>
《UML用户指南(第2版.修订版)》—第2章2.4节软件开发生命周期
查看>>
《师兄教你找工作——100场面试 20个offer背后的求职秘密》一2.5 那些老生常谈的问题...
查看>>
人类基因编辑国际峰会周琪院士谈基因编辑的未来
查看>>
苹果公然与FBI叫板背后:美国大哥都监控了什么?
查看>>
《Axure RP8 网站和APP原型制作 从入门到精通》一1.3 总结
查看>>
《jQuery Cookbook中文版》——1.1 在HTML页面中包含jQuery程序库代码
查看>>
《 软件测试价值提升之路》——第3章 拦截缺陷 3.1 用户无法正常使用
查看>>
XP系统的共享,你究竟知道……
查看>>
开源大数据周刊-第12期
查看>>
《卸甲笔记》-PostgreSQL和Oracle的SQL差异分析之五:函数的差异(一)
查看>>