把出现线程安全问题的核心代码给上锁:(每次只能进入一个线程,相当于给线程排队,一个一个进)
synchronized [ˈsɪŋkrənaɪzd] 同步的意思 这个锁没有特别的意思,就是一个flag的标志
作为锁: ()里面写“itheima”也可以,因为是在字符串常量池中,只有一份

上锁快捷键:ctrl + t 选第9个
使用共享资源来定义锁对象, 不要使用唯一的对象来作为锁的对象
实例方法用this代表锁对象(实现共享,是调用就是哪个对象)
静态方法使用 :类名.class(将其锁住) 类名.class 对于所有线程来说都是唯一 一个

package com.itheima.d3_thread_safe;/*** 需求: 模拟取钱案例。*/public class ThreadDemo {public static void main(String[] args) {// 1. 定义线程类,创建一个共享的账户对象Account acc = new Account("ICBC-111",100000);// 创建2个线程对象,代表小明和小红同时进来了// 线程启动new DrawThread(acc,"小明").start(); // 这是一种匿名写法(不要写对象名,直接创建出来对象后调用其方法)new DrawThread(acc,"小红").start(); // 这是一种匿名写法(不要写对象名,直接创建出来对象后调用其方法)// 相当于定义了两家人 (如果定义的锁对象一样,相当于所有的线程都走一样对象的锁,账户没有分开,每个账户公用一把锁)// 会影响其他无关线程的执行// 1. 定义线程类,创建一个共享的账户对象Account acc2 = new Account("ICBC-112",100000);// 创建2个线程对象,代表小明和小红同时进来了// 线程启动new DrawThread(acc2,"小黑").start(); // 这是一种匿名写法(不要写对象名,直接创建出来对象后调用其方法)new DrawThread(acc2,"小白").start(); // 这是一种匿名写法(不要写对象名,直接创建出来对象后调用其方法)}}
package com.itheima.d3_thread_safe;/*** 取钱的线程类*/public class DrawThread extends Thread{// 接收处理的账户对象private Account acc;// 定义有参和无参构造器// 无参一般要自己写public DrawThread(){}public DrawThread(Account acc,String name) { // 利用有参构造器为父类Thread线程类取名字super(name); // 这里的super相当于是父类Thread线程类,直接为该线程类取名this.acc = acc;}// 继承了线程类后:一定要重写run方法@Overridepublic void run() {// 该线程定义: 小红,小明: 取钱acc.drawMoney(100000); // 该drawMoney方法定义在Account账户类里面}}
package com.itheima.d3_thread_safe;public class Account {private String cardId;private double money; // 账户余额public Account() {}public Account(String cardId, double money) {this.cardId = cardId;this.money = money;}public String getCardId() {return cardId;}public void setCardId(String cardId) {this.cardId = cardId;}public double getMoney() {return money;}public void setMoney(double money) {this.money = money;}// 100个人调用该线程,只有一个线程能占用这个类 -- 其他99个线程在外面等他 (因为调用该方法要用到类名.方法,然而类名被锁住了,所以只有一个人能解锁锁)public static void run(){ // 由于这个方法是拿账户(Account类)调用,对于所有线程来说都是唯一的方法// 所以直接把类名锁起来就可以了,synchronized (Account.class){}}/*** 在账户类里面定义一个取钱方法drawMoney* @param money 代表用户取的钱*/public void drawMoney(int money) {// 0. 先获取是谁来取钱,线程的名字就是人嘛// 使用Thread.currentThread() 获取当前线程对象(谁调用该线程,就获取哪个线程对象),然后使用getNmae方法获取该线程名字String name = Thread.currentThread().getName();// 同步代码块: 给会出现线程安全问题的代码块给上锁// synchronized ("heima") 一样的,都是唯一对象,这个字符串对象都在常量池中,表唯一// 将字符串对象,改成 this == acc(表示当前对象,当前调用drawMoney方法的对象,这样就不会让账户冲突// this == acc 共享账户 不会让账户冲突 -- 如果是小明小红账户取钱(调用drawMoney方法),那this代表小明小红(这样就实现了共享)synchronized (this) { // 直接双引号:因为会在字符串常量池中产生(唯一的一份),满足了锁的唯一性(锁用任意唯一的对象不好)// 1. 判断账户是否够钱if (this.money >= money){// 2. 取钱System.out.println(name + "来取钱成功,吐出:" + money);// 3. 更新余额this.money -= money;System.out.println(name + "取钱后余额剩余:" + this.money);}else {// 余额不足System.out.println(name + "来取钱,余额不足");}}}}
