Java基础知识系列 多线程基础之一

系列的几篇文章介绍一下Java语言的多线程的使用。

首先,模拟一个场景,多个线程同时操纵一个账户(Account),有些进行取钱操作,有些进行存钱操作。

每次取钱金额为500,不足500无法取钱。
每次存钱金额100,账户余额大于等于500无法存钱。

取钱时金额不足,取钱的线程会等待,等其他存钱的线程存够500则可以继续取钱。
存钱时账户已经有500,则存钱的线程会等待,等其他取钱的线程取走钱后可以继续存钱。

首先定义一个账户类
账户有个withdraw方法,用来对账户进行取500元的操作。
当账户余额不足,则取钱的线程会等待。

账户有个deposit方法,用来对账户进行存100元钱的操作。
当账户余额已达500,则存钱的线程会等待。

class Account {
        int balence = 500;

        synchronized void withdraw(int amount) throws InterruptedException {
            while (this.balence < amount) {
                System.out.println("线程 " + Thread.currentThread().getName() + "在尝试取钱 ");
                System.out.println("尝试取钱 " + amount + ", 余额为 " + this.balence+"无法取钱");
                wait();
            }
            this.balence -= amount;
            notifyAll();//若成功取钱,则通知其他线程取消等待
            System.out.println("取钱 " + amount + ", 余额为 " + this.balence);
        }

        synchronized void deposit(int amount) throws InterruptedException {
            while (this.balence >= 500) {
                System.out.println("线程 " + Thread.currentThread().getName() + "在尝试存钱 ");
                System.out.println("尝试存钱 " + amount + ", 余额为 " + this.balence+"无法存钱");
                wait();
            }
            this.balence += amount;
            notifyAll();//若成功存钱,则通知其他线程取消等待
            System.out.println("存钱 " + amount + ", 余额为 " + this.balence);
        }
    }

初始时账户中有500余额。

然后定义一个取钱操作
一旦放入一个线程中启动,线程一直会对这个账户进行取钱操作

    Runnable withdraw = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    account.withdraw(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

再定义一个存钱操作
一旦放入一个线程中启动,线程一直会对这个账户进行存钱操作

    Runnable deposit = new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    account.deposit(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

再开启多个线程同时操作账户进行取钱和存钱的操作,再main函数中调用。

    void accountTest() {
        Thread t1 = new Thread(this.withdraw);
        Thread t11 = new Thread(this.withdraw);
        Thread t12 = new Thread(this.withdraw);
        Thread t13 = new Thread(this.withdraw);
        Thread t14 = new Thread(this.withdraw);
        Thread t2 = new Thread(this.deposit);
        Thread t21 = new Thread(this.deposit);
        Thread t22 = new Thread(this.deposit);
        Thread t23 = new Thread(this.deposit);
        Thread t24 = new Thread(this.deposit);
        t1.start();
        t11.start();
        t12.start();
        t13.start();
        t14.start();
        t2.start();
        t21.start();
        t22.start();
        t23.start();
        t24.start();
    }

完整的代码如下

package com.basic.thread;

public class ThreadTest1 {

    Account account = new Account();// 线程安全的账户

    public static void main(String[] args) {
        new ThreadTest1().accountTest();
    }

    class Account {
        int balence = 500;

        synchronized void withdraw(final int amount) throws InterruptedException {
            while (this.balence < amount) {
                System.out.println("线程 " + Thread.currentThread().getName() + "在尝试取钱 ");
                System.out.println("尝试取钱 " + amount + ", 余额为 " + this.balence+"无法取钱");
                wait();
            }
            this.balence -= amount;
            notifyAll();
            System.out.println("取钱 " + amount + ", 余额为 " + this.balence);
        }

        synchronized void deposit(final int amount) throws InterruptedException {
            while (this.balence >= 500) {
                System.out.println("线程 " + Thread.currentThread().getName() + "在尝试存钱 ");
                System.out.println("尝试存钱 " + amount + ", 余额为 " + this.balence+"无法存钱");
                wait();
            }
            this.balence += amount;
            notifyAll();
            System.out.println("存钱 " + amount + ", 余额为 " + this.balence);
        }
    }

    Runnable withdraw = new Runnable() {
        @Override
        public void run() {
            try {
                while (true) {
                    account.withdraw(500);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    Runnable deposit = new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    account.deposit(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    void accountTest() {
        Thread t1 = new Thread(this.withdraw);
        Thread t11 = new Thread(this.withdraw);
        Thread t12 = new Thread(this.withdraw);
        Thread t13 = new Thread(this.withdraw);
        Thread t14 = new Thread(this.withdraw);
        Thread t2 = new Thread(this.deposit);
        Thread t21 = new Thread(this.deposit);
        Thread t22 = new Thread(this.deposit);
        Thread t23 = new Thread(this.deposit);
        Thread t24 = new Thread(this.deposit);
        t1.start();
        t11.start();
        t12.start();
        t13.start();
        t14.start();
        t2.start();
        t21.start();
        t22.start();
        t23.start();
        t24.start();
    }
}

运行这个程序,查看console输出的信息:

//省略以上部分
线程 Thread-0在尝试取钱 
尝试取钱 500, 余额为 0无法取钱
存钱 100, 余额为 100
存钱 100, 余额为 200
存钱 100, 余额为 300
存钱 100, 余额为 400
存钱 100, 余额为 500
线程 Thread-8在尝试存钱 
尝试存钱 100, 余额为 500无法存钱
线程 Thread-9在尝试存钱 
尝试存钱 100, 余额为 500无法存钱
线程 Thread-7在尝试存钱 
尝试存钱 100, 余额为 500无法存钱
线程 Thread-6在尝试存钱 
尝试存钱 100, 余额为 500无法存钱
线程 Thread-5在尝试存钱 
尝试存钱 100, 余额为 500无法存钱
取钱 500, 余额为 0
线程 Thread-3在尝试取钱 
尝试取钱 500, 余额为 0无法取钱
线程 Thread-4在尝试取钱 
尝试取钱 500, 余额为 0无法取钱
线程 Thread-2在尝试取钱 
尝试取钱 500, 余额为 0无法取钱
存钱 100, 余额为 100
存钱 100, 余额为 200
存钱 100, 余额为 300
存钱 100, 余额为 400
存钱 100, 余额为 500
//省略以下部分

通过结果可以看到,同时操作的账户能正确地处理多个存取操作。

这里注意到,Account的deposit与withdraw方法使用了同步方法,并通过wait-notify进行线程间通信,保证了线程安全。

线程安全问题出现的情况请查看下一篇

Java基础知识系列 多线程基础之二 线程安全

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 账户(满分50 分) 版本1:满分10 分 设计 Account1 ,包含: ■ 一个名为id 的int 型的私有数据域(默认值为0),长度为6 位。 ■ 一个名为balance 的double 型的私有数据域(默认值为0)。 ■ 一个名为annualInterestRate 的double 型的私有数据域存储当前利率(默认值为0)。 假设所有的账户都有相同的利率。 ■ 一个名为dateCreated 的Date 型的私有数据域存储账户的开户日期。 ■ 一个能创建默认账户的无参构造方法。 ■ 一个能创建带特定id 和初始余额的构造方法,初始余额不能为负数。 ■ id、balance 和annualInterestRate 的访问器和修改器。 ■ dateCreated 的访问器。 ■ 一个名为getMonthlyInterestRate 的方法返回月利率。 ■ 一个名为withDraw 的方法账户提取特定金额。 ■ 一个名为deposit 的方法账户存人特定金额。 ■ double 型的数据域保留2 位小数。 ■ 成员方法和数据域应进行基本的合理性检查。 设计测试ATMMachine1: ■ 创建一个有100 个账户的数组,其id 为0,1,2,...99, 并初始化收支为1000 美元。 ■ 主菜单如下(可参考教材中文版P296 或英文版P367): Main menu 1: check balance 2: withdraw 3: deposit 4: exit 版本2:满分20 分 扩展 Account1 为Account2 : ■ Account2 继承Account1 。 ■ 为Account2 新增一个名为password 的String 型的私有数据域存储账号密码。 password 只能为字母或数字,长度不能小于6 且不能大于10。密码显示时为*******。 ■ 为Account2 新增一个名为name 的String 型的私有数据域存储客户名字。 ■ 为Account2 新增一个名为transactions 的ArrayList 型的新数据域,其为客户存 储交易记录。这要求新建一个名为Transaction 的的定义请参照教材中文版P327 或英 文版P404。每笔交易都是Transaction 一个实例。 ■ 新增一个带初始余额的构造方法,其id 随机产生,但不能与当前系统的id 重复。 若初始余额的参数为负数,则抛出一个自定义异常并在当前构造方法中进行处理。 ■ 重写方法withDraw,要求支取的金额为100 的整数倍,并且当日支取金额不能超过 5000,支取金额不允许透支。每进行一次操作应向transactions 数组线性表添加一笔交易。 ■ 重写方法deposit,要求每进行一次操作应向transactions 数组线性表添加一笔交易。 ■ 新增一个方法changePassword,只有旧密码正确,新密码符合要求,且两次输入相 同的情况下才可以成功修改密码 设计测试ATMMachine2,其主菜单如下(可参考教材中文版P296 或英文版P367): Main menu 0:create a account 1: check balance 2: withdraw 3: deposit 4:details of the transaction 5: change password 6:exit ■ 若用户选择新建一个账号, 则应提示用户输入账号password 、balance 和 annualInterestRate,其中id 随机产生。新产生的账户应序列化到名为accounts.dat 的文件中。 所有账户只能通过这种方式产生。 ■ 所有用户操作结果应同步到accounts.dat 文件中相应账户中。 ■ 所有用户操作应有友好、简介的提示语。 版本3:满分20 分 请参照银行的ATM 机界面,在Account2 基础上开发一个GUI 界面的ATM 系统。 要求界面应模拟小键盘,并且账户信息读、写于文件accounts.dat。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值