线程安全
多个线程,同时操作同一个共享资源的时候,可能会出现安全问题。
例如:两个人来取钱的案例
public class test {
public static void main(String[] args) {
// 1. 创建一个账户对象。代表账户人的共享账户。
Account acc = new Account( "ICBC-110", 100000);
// 2. 创建两个线程,代表两个代理同时操作该账户,一会取100,一会取200。
new DrawThread(acc, "小明").start(); // 小明
new DrawThread(acc, "小红").start(); // 小红
}
}
线程启动
public class DrawThread extends Thread {
private Account acc;
public DrawThread(Account acc, String name) {
super(name);
this.acc = acc;
}
@Override
public void run() {
acc.drawMoney(100000);
}
}
取钱账号及构造器
public class Account {
private String cardId;
private double money;
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
// 小明和小红可以过来
public void drawMoney(double money) {
// 先知道是谁来取钱
String name = Thread.currentThread().getName();
// 1. 判断账户当前余额是否足够
if (this.money >= money) {
System.out.println(name + "来取钱成功,吐出:" + money);
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
} else {
System.out.println(name + "来取钱,余额不足!");
}
}
public String getCardId() { return cardId; }
public void setCardId(String cardId) { this.cardId = cardId; }
public double getMoney() { return money; }
}
线程同步
目的:解决线程安全问题。
思想:让多个线程先后一次访问共享资源,这样就解决了安全问题。
常见方案:
加锁
每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能加锁进来。
方法一、同步代码块(synchronized)
把访问共享资源的核心代码给上锁,以此保证线程安全。
this只会影响同一个账户下的用户,不会影响不同用户的账户。
synchronized ("lock") {
if (this.money >= money) {
System.out.println(name + "来取钱成功,吐出:" + money);
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
} else {
System.out.println(name + "来取钱,余额不足!");
}
}
ctrl alt t 就可以上锁。
对于静态对象来说
synchronized ("lock") {
if (this.money >= money) {
System.out.println(name + "来取钱成功,吐出:" + money);
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
} else {
System.out.println(name + "来取钱,余额不足!");
}
}
方法二、同步方法
作用:把访问资源的和核心方法给上锁,以此保证线程安全。
原理同上
public synchronized void drawMoney(double money) {
// 先知道是谁来取钱
String name = Thread.currentThread().getName();
// 1. 判断账户当前余额是否足够
if (this.money >= money) {
System.out.println(name + "来取钱成功,吐出:" + money);
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
} else {
System.out.println(name + "来取钱,余额不足!");
}
}
底层原理;
同步方法其实也是有隐式锁对象的,只是所得范围是整个方法代码。
如果方法是实例方法,同步方法默认使用this作为锁对象。
如果是静态方法:同步方法默认使用类名.class作为的锁对象。
同步代码块月同步方法比较:
同步代码块锁的范围更小,大家都可以先加载方法,再执行没有被锁的代码,最后分批执行被锁代码,所以效率更高。
方法三、Lock锁
程序员手动上锁,通过他可以创建出锁对象进行加锁和解锁,更强大、灵活、方便。
Lock是接口,不能实例化,要使用他的实现类ReentrantLock来创建Lock锁对象。
public class Account {
private String cardId;
private double money;
private final Lock lk = new ReentrantLock();
public Account(String cardId, double money) {
this.cardId = cardId;
this.money = money;
}
//public static void test(){
// synchronized (Account.class){
//
// }
//}
// 小明和小红可以过来
public void drawMoney(double money) {
// 先知道是谁来取钱
String name = Thread.currentThread().getName();
// 1. 判断账户当前余额是否足够
try {
lk.lock();
if (this.money >= money) {
System.out.println(name + "来取钱成功,吐出:" + money);
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
} else {
System.out.println(name + "来取钱,余额不足!");
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {//防止出现错误,一直被锁
lk.unlock();
}
}
标签:name,money,System,安全,线程,println,多线程,public,out
From: https://blog.51cto.com/u_16382144/11938432