笔记
线程安全问题:多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题
例如下面这段代码
public class Account {
private String id;
private double money;
public Account() {
}
public Account(String id, double money) {
this.id = id;
this.money = money;
}
public void DrawMoney(double money) {
String name= Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
this.money-=money;
System.out.println("余额"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
public void setId(String id) {
this.id = id;
}
public void setMoney(double money) {
this.money = money;
}
public String getId() {
return id;
}
public double getMoney() {
return money;
}
}
public class MyThread extends Thread{
private Account acc;
public MyThread(String name,Account acc){
super(name);
this.acc=acc;
}
@Override
public void run() {
acc.DrawMoney(100000);
}
}
public class Test {
public static void main(String[] args) {
Account account=new Account("1314",100000);
new MyThread("小明",account).start();
new MyThread("小红",account).start();
}
}
账户内只有100000元,当两个人同时取钱时,若小明取完钱,账户内的余额还未更新,小红又取了钱,那么此时两人都取了100000元(血赚),为了防止这种问题出现,就有了线程安全。那么如何实现线程安全呢。
1、synchronized同步代码块
public void DrawMoney(double money) {
String name=Thread.currentThread().getName();
//同步代码块
synchronized (this){
if (this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
this.money-=money;
System.out.println("余额"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}}
这里对出现的问题核心代码使用synchronized进行加锁 ,每次只能有一个线程占锁进行访问,此时就实现了线程安全。
锁对象的要求:1、对于实例方法建议使用this做为锁对象
2、对于静态方法建议使用字节码(类名.class)对象做为锁对象
2、使用synchronized修饰方法
public synchronized void DrawMoney(double money) {
String name= Thread.currentThread().getName();
if (this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
this.money-=money;
System.out.println("余额"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}
}
这种方法比同步代码快简约,但性能比同步代码块差一点,程序员可根据不同的使用场景进行选择运用。
3、创建锁对象的方法(这种方法很专业)
这里只展示账户类的代码,测试类和线程类和第一个代码段一样
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private String id;
private double money;
private final Lock lock=new ReentrantLock();
//锁对象用final修饰,唯一且不可替换,非常专业
public Account() {
}
public Account(String id, double money) {
this.id = id;
this.money = money;
}
public void DrawMoney(double money) {
String name=Thread.currentThread().getName();
lock.lock();//上锁
try{
if (this.money>=money){
System.out.println(name+"取钱成功,吐出"+money);
this.money-=money;
System.out.println("余额"+this.money);
}else {
System.out.println(name+"来取钱,余额不足");
}}finally {
lock.unlock();//解锁
}
}
public void setId(String id) {
this.id = id;
}
public void setMoney(double money) {
this.money = money;
}
public String getId() {
return id;
}
public double getMoney() {
return money;
}
}
总结:以上就是线程安全的三种实现方法
线程安全问题出现的原因:存在多线程并发;同时访问共享资源;同时修改共享资源。
笔记结束