java多线程入门附银行取钱案例(上)

169 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

1. 多线程相关的概念

  • 进程:软件静态文件包,系统上程序的动态过程,占用系统资源(CPU、内存、硬盘),进程崩了,会释放资源
  • 线程:进程里面最小的执行单元,一个线程有一个自己独立的栈,线程和进程之间可以共享包含他们的进程的空间堆、方法区;线程和线程的栈空间不会,局部变量不会共享;某个线程崩了进程也会崩掉,JVM虚拟机可以开多个线程,线程可以并发执行

2. java中的多线程

  • Java中的线程 --> Thread线程类
  • package java.lang;
  • class Thread implements Runnable;

2.1 run 方法与Start方法的区别

  • run()方法知识一个普通的方法,线程对象里面都需要重写它,线程对象.start()开启了一个新的线程,自动调用重写的run()

3. 线程的生命周期

进程中最小执行单元的执行过程,一个 线程拥有一个独立的栈空间

  • 新建: new Thread()
  • 就绪: 就绪队列排队,抢夺CPU时间片(Java是抢夺式模式)
  • 运行: 抢到CPU时间片,就会进入运行状态,当某个线程的CPU时间片用完之后就会回到就绪状态
  • 阻塞: 阻塞事件(输入事件、sleep睡眠)主动让出手里的CPU时间片,等待阻塞事件结束回到就绪状态,继续排队抢夺时间片
  • 结束: exit();

多线程执行过程.png

4. 多线程的实现方式

4.1 实现方式

  • 方式一:自定义类继承的方式Thread,重写run()方法 用的不多
  • 方式二:自定义类实现接口Runnable接口 更灵活

4.2常用方法

  1. public static Thread currentThread() 返回当前线程对象的引用

  2. public final String getName() 返回线程的名称

  3. public final void setName(String name) 设置线程的名称

  4. main线程默认名称是"main", 其他线程默认名称是"Thread-序号",序号从0开始以1递增

  5. public static void sleep(long millis) 以传入的毫秒数睡眠 ;

    5.1 注意 :静态方法,以当前线程为准,进行方法调用

    5.2 醒来之后执行睡前的代码,而不是从头开始

    5.3 sleep 需要处理异常,异常不能上抛,只能try,catch 处理,原因:实现了Runnable重写了run方法,子类重写父类的方法不能抛出比父类更广的异常

5. 线程安全

5.1 多线程并发(同时执行)发生数据安全的条件

  1. 系统支持多线程同时执行
  2. 共享数据
  3. 修改(增、减)数据,读取不影响

5.2 解决线程安全的方法:排队

  • 同步:synchronized(共享对象)

    原理:前面程序没有执行完,后面程序不能执行,效率低,线性安全

附:一步概念 :异步:asynchronized:两端程序同时执行,互不干扰,效率高,有可能不安全

案例:银行(ATM)取钱

演示说明:两个人用同一张卡去银行取钱,如果没有解决线性安全,会出现什么情况,解决了 线性安全又会出现什么情况

看一张分析图

银行取钱(多线程).png

  1. 代码演示:

//Account :银行卡类
​
package classes.accountpro;
​
public class Account {
    private String name;
    private double balance;
​
    public Account() {
    }
​
    public Account(String name, double balance) {
        this.name = name;
        this.balance = balance;
    }
​
//    取钱方法
​
    public void getMoney (double money) {
        synchronized(this){
            setBalance(this.balance-money);
            System.out.println(Thread.currentThread().getName()+"卡上余额为:"+this.balance);
        }
    }
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public double getBalance() {
        return balance;
    }
​
    public void setBalance(double balance) {
        this.balance = balance;
    }
}
// 测试类,这里用的是 Extends 继承Thread 创建的线程
package classes.accountpro;
​
public class Text {
    public static void main(String[] args) {
        Account account = new Account("1243571239",10000);
//        Ta ta = new Ta(account);
        Thread ta = new Ta(account);
        Thread tb = new Tb(account);
​
        ta.setName("ta");
        tb.setName("tb");
        ta.start();
        tb.start();
        tc.start();
​
    }
}
​
class Ta extends Thread{
​
    private Account act;
​
    public Ta(Account act) {
        this.act = act;
    }
​
    @Override
    public void run() {
        double money = 5000;
        act.getMoney(money);
    }
}
class Tb extends Thread{
​
    private Account act;
​
    public Tb(Account act) {
        this.act = act;
    }
​
    @Override
    public void run() {
        double money = 5000;
        act.getMoney(money);
    }
}

运行结果图:出现了四种可能

银行取钱可能出现的四种情况.png

总结

通过案例分析,线程的知识还是有些清晰的,但是线程问题并不好解决,多线程,就会出现很多不符合常规的情况,可以通过synchronize 进行解决,问题解决了但是这又影响了效率,还有一些问题,如何让所有线程都同步???sleep 在什么场景下使用呢? 还要继续学习!

\