线程安全出现的原因
1、存在多线程并发
2、同时访问共享资源并且修改共享资源
如何解决: 让多个线程实现先后依次访问共享资源,这样就可以解决安全问题 线程同步的核心思想
加锁:把共享资源进行上锁,每次只能一个线程进入访问完毕后,然后其他线程才能进来
方案一(同步代码块)
作用:把出现线程安全问题的核心代码给上锁
原理:每次只能有一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行
public void dram(double money) {
String sr = Thread.currentThread().getName();
synchronized (this) {//对其上锁
if(this.money >= money){
System.out.println(sr + "取走了" + money);
this.money -= money;
System.out.println(sr + "取钱后余额为" + this.money);
}else {
System.out.println(sr + "来取钱 余额不足");
}
}
}
锁对象的要求
理论上:锁对象只要对于当前同时执行的线程来说是同一个对象即可,但是这样并不好,会影响其他无关线程的执行
锁对象的规范要求
1、建议使用共享资源作为锁对象
2、对于实例方法建议使用this作为锁对象
3、对于静态方法建议使用字节码(类名.class)对象作为锁对象
方案二(同步方法)
原理:每次只能有一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行
public synchronized void dram(double money) {
String sr = Thread.currentThread().getName();
if(this.money >= money){
System.out.println(sr + "取走了" + money);
this.money -= money;
System.out.println(sr + "取钱后余额为" + this.money);
}else {
System.out.println(sr + "来取钱 余额不足");
}
}
同步方法底层原理
1、同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码
2、如果方法是实例方法:同步方法默认用this作为锁的对象,但是代码要高度面向对象
3、如果方法是静态方法:同步方法默认用类名.class锁的对象
方案三(LOCK锁)
public final ReentrantLock LOCK = new ReentrantLock(); //建议使用final修饰 因为锁对象的唯一性
public synchronized void dram(double money) {
LOCK.lock();//对代码进行上锁
String sr = Thread.currentThread().getName();
try { //try-finally捕获异常
if(this.money >= money){
System.out.println(sr + "取走了" + money);
this.money -= money;
System.out.println(sr + "取钱后余额为" + this.money);
}else {
System.out.println(sr + "来取钱 余额不足");
}
} finally {//放在try-finally里不管是否抛出异常都会对其进行解锁
LOCK.unlock();
}
}