JavaSE笔记25:多线程(三)

本文深入探讨Java中的线程安全问题及解决方案,包括同步机制、死锁、Lock锁的使用,详细解释了synchronized关键字的同步代码块和方法,以及wait和notify方法在生产者消费者模式中的应用。
摘要由CSDN通过智能技术生成

多线程(三)

线程安全

什么时候数据在多线程并发的环境下会存在安全问题

  1. 多线程并发
  2. 有共享数据
  3. 共享数据有修改行为

满足以上3个条件之后,就会存在线程安全问题

同步机制

如何解决?线程排队执行(不能并发),专业术语叫做“线程同步机制”

​ 用排队执行解决线程安全问题,这种机制被称为:线程同步机制。

注意:线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全第一位。只有数据安全了,我们才可以谈效率,数据不安全,没有效率的事。

说到线程同步这块,涉及到两个专业术语:

  1. 异步编程模型:

    线程T1和线程T2,各自执行各自的,T1不管T2,T2不管T1,谁也不需要等谁,其实就是多线程并发(效率较高)

  2. 同步编程模型:

    线程T1和T2,在线程T1执行的时候,必须等待T2线程执行结束,或者说在T2线程执行的时候,必须等待T1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。(效率较低,线程排队执行)

异步就是并发,同步就是排队!!!

synchronized的写法:

  1. 同步代码块
synchronized(线程共享对象){
   
    同步代码块;
}
  1. 在实例方法上使用synchronized。表示共享对象一定是this,并且同步代码块是整个方法体。

  2. 在静态方法上使用synchronized。表示找类锁,类锁永远只有1把。

    对象锁:1个对象1把锁,100个对象100把锁。

    类锁:100个对象,也可能只是1把类锁

例子1
package se5.threadsafe;

public class Test {
   
    public static void main(String[] args) {
   
        //创建账户对象(只创建一个)
        Account act = new Account("act-001",10000);
        //创建两个线程
        Thread t1 = new AccountThread(act);
        Thread t2 = new AccountThread(act);
        //设置name
        t1.setName("t1");
        t2.setName("t2");
        //启动线程取款
        t1.start();
        t2.start();
        /**
         * 结果:
         * t1对act-001取款5000.0成功,余额5000.0
         * t2对act-001取款5000.0成功,余额5000.0
         */
    }
}
package se5.threadsafe;
/*
银行账户
    不使用线程同步机制,多线程对同一个账户进行取款,出现线程安全问题
 */
public class Account {
   
    //账号
    private String actno;
    //余额
    private double balance;

    public Account() {
   }
    public Account(String actno, double balance) {
   
        this.actno = actno;
        this.balance = balance;
    }

    public String getActno() {
   
        return actno;
    }

    public void setActno(String actno) {
   
        this.actno = actno;
    }

    public double getBalance() {
   
        return balance;
    }

    public void setBalance(double balance) {
   
        this.balance = balance;
    }

    //取款方法
    public void withdraw(double money){
   
        //t1和t2并发这个方法。。。(t1和t2是两个栈,两个栈操作堆中同一个对象)
        //取款之前的余额
        double before = this.getBalance();
        //取款之后的余额
        double after = before - money;

        //在这里模拟一下网络延迟,100%出问题
        try {
   
            Thread.sleep(1000);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }

        //更新余额
        //思考:t1执行到这里了,但还没有执行这行代码,t2线程进来withdraw方法了,此时一定出问题。
        this.setBalance(after);
    }
}
package se5.threadsafe;

public class AccountThread extends Thread {
   
    //两个线程必须共享同一个账户对象
    private Account act;
    //通过构造方法传递过来账户对象
    public AccountThread(Account act){
   
        this.act = act;
    }
    public void run(){
   
        //run方法的执行表示取款操作
        //假设取款5000
        double money = 5000;
        //取款
        //多线程并发执行这个方法
        act.withdraw(money);
        System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款" + money + "成功,余额" + act.getBalance());
    }
}

修改后

package se5.threadsafe2;

public class Test {
   
    public static void main(String[] args) {
   
        //创建账户对象(只创建一个)
        Account act = new Account("act-001",10000)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值